/* * Parse a Bootstrap Records (used for restores) * * Kern Sibbald, June MMII * * Version $Id: parse_bsr.c 4992 2007-06-07 14:46:43Z kerns $ */ /* Bacula® - The Network Backup Solution Copyright (C) 2002-2006 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 modify it under the terms of version two of the GNU General Public License as published by the Free Software Foundation and included in the file LICENSE. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 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 along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Bacula® is a registered trademark of John Walker. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ #include "bacula.h" #include "stored.h" typedef BSR * (ITEM_HANDLER)(LEX *lc, BSR *bsr); static BSR *store_vol(LEX *lc, BSR *bsr); static BSR *store_mediatype(LEX *lc, BSR *bsr); static BSR *store_device(LEX *lc, BSR *bsr); static BSR *store_client(LEX *lc, BSR *bsr); static BSR *store_job(LEX *lc, BSR *bsr); static BSR *store_jobid(LEX *lc, BSR *bsr); static BSR *store_count(LEX *lc, BSR *bsr); static BSR *store_jobtype(LEX *lc, BSR *bsr); static BSR *store_joblevel(LEX *lc, BSR *bsr); static BSR *store_findex(LEX *lc, BSR *bsr); static BSR *store_sessid(LEX *lc, BSR *bsr); static BSR *store_volfile(LEX *lc, BSR *bsr); static BSR *store_volblock(LEX *lc, BSR *bsr); static BSR *store_sesstime(LEX *lc, BSR *bsr); static BSR *store_include(LEX *lc, BSR *bsr); 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 bool is_fast_rejection_ok(BSR *bsr); static bool is_positioning_ok(BSR *bsr); struct kw_items { const char *name; ITEM_HANDLER *handler; }; /* * List of all keywords permitted in bsr files and their handlers */ struct kw_items items[] = { {"volume", store_vol}, {"mediatype", store_mediatype}, {"client", store_client}, {"job", store_job}, {"jobid", store_jobid}, {"count", store_count}, {"fileindex", store_findex}, {"jobtype", store_jobtype}, {"joblevel", store_joblevel}, {"volsessionid", store_sessid}, {"volsessiontime", store_sesstime}, {"include", store_include}, {"exclude", store_exclude}, {"volfile", store_volfile}, {"volblock", store_volblock}, {"stream", store_stream}, {"slot", store_slot}, {"device", store_device}, {NULL, NULL} }; /* * Create a BSR record */ static BSR *new_bsr() { BSR *bsr = (BSR *)malloc(sizeof(BSR)); memset(bsr, 0, sizeof(BSR)); return bsr; } /* * Format a scanner error message */ static void s_err(const char *file, int line, LEX *lc, const char *msg, ...) { JCR *jcr = (JCR *)(lc->caller_ctx); va_list arg_ptr; char buf[MAXSTRING]; va_start(arg_ptr, msg); bvsnprintf(buf, sizeof(buf), msg, arg_ptr); va_end(arg_ptr); if (jcr) { Jmsg(jcr, M_FATAL, 0, _("Bootstrap file error: %s\n" " : Line %d, col %d of file %s\n%s\n"), buf, lc->line_no, lc->col_no, lc->fname, lc->line); } else { e_msg(file, line, M_FATAL, 0, _("Bootstrap file error: %s\n" " : Line %d, col %d of file %s\n%s\n"), buf, lc->line_no, lc->col_no, lc->fname, lc->line); } } /********************************************************************* * * Parse Bootstrap file * */ BSR *parse_bsr(JCR *jcr, char *fname) { LEX *lc = NULL; int token, i; BSR *root_bsr = new_bsr(); BSR *bsr = root_bsr; Dmsg1(300, "Enter parse_bsf %s\n", fname); if ((lc = lex_open_file(lc, fname, s_err)) == NULL) { berrno be; Emsg2(M_ERROR_TERM, 0, _("Cannot open bootstrap file %s: %s\n"), fname, be.bstrerror()); } lc->caller_ctx = (void *)jcr; while ((token=lex_get_token(lc, T_ALL)) != T_EOF) { Dmsg1(300, "parse got token=%s\n", lex_tok_to_str(token)); if (token == T_EOL) { continue; } for (i=0; items[i].name; i++) { if (strcasecmp(items[i].name, lc->str) == 0) { token = lex_get_token(lc, T_ALL); Dmsg1 (300, "in T_IDENT got token=%s\n", lex_tok_to_str(token)); if (token != T_EQUALS) { scan_err1(lc, "expected an equals, got: %s", lc->str); bsr = NULL; break; } Dmsg1(300, "calling handler for %s\n", items[i].name); /* Call item handler */ bsr = items[i].handler(lc, bsr); i = -1; break; } } if (i >= 0) { Dmsg1(300, "Keyword = %s\n", lc->str); scan_err1(lc, "Keyword %s not found", lc->str); bsr = NULL; break; } if (!bsr) { break; } } lc = lex_close_file(lc); Dmsg0(300, "Leave parse_bsf()\n"); if (!bsr) { free_bsr(root_bsr); root_bsr = NULL; } if (root_bsr) { root_bsr->use_fast_rejection = is_fast_rejection_ok(root_bsr); root_bsr->use_positioning = is_positioning_ok(root_bsr); } for (bsr=root_bsr; bsr; bsr=bsr->next) { bsr->root = root_bsr; } return root_bsr; } static bool is_fast_rejection_ok(BSR *bsr) { /* * Although, this can be optimized, for the moment, require * all bsrs to have both sesstime and sessid set before * we do fast rejection. */ for ( ; bsr; bsr=bsr->next) { if (!(bsr->sesstime && bsr->sessid)) { return false; } } return true; } static bool is_positioning_ok(BSR *bsr) { /* * Every bsr should have a volfile entry and a volblock entry * if we are going to use positioning */ for ( ; bsr; bsr=bsr->next) { if (!bsr->volfile || !bsr->volblock) { return false; } } return true; } static BSR *store_vol(LEX *lc, BSR *bsr) { int token; BSR_VOLUME *volume; char *p, *n; token = lex_get_token(lc, T_STRING); if (token == T_ERROR) { return NULL; } if (bsr->volume) { bsr->next = new_bsr(); bsr = bsr->next; } /* This may actually be more than one volume separated by a | * If so, separate them. */ for (p=lc->str; p && *p; ) { n = strchr(p, '|'); if (n) { *n++ = 0; } volume = (BSR_VOLUME *)malloc(sizeof(BSR_VOLUME)); memset(volume, 0, sizeof(BSR_VOLUME)); bstrncpy(volume->VolumeName, p, sizeof(volume->VolumeName)); /* Add it to the end of the volume chain */ if (!bsr->volume) { bsr->volume = volume; } else { BSR_VOLUME *bc = bsr->volume; for ( ;bc->next; bc=bc->next) { } bc->next = volume; } p = n; } return bsr; } /* Shove the MediaType in each Volume in the current bsr */ static BSR *store_mediatype(LEX *lc, BSR *bsr) { int token; token = lex_get_token(lc, T_STRING); if (token == T_ERROR) { return NULL; } if (!bsr->volume) { Emsg1(M_ERROR,0, _("MediaType %s in bsr at inappropriate place.\n"), lc->str); return bsr; } BSR_VOLUME *bv; for (bv=bsr->volume; bv; bv=bv->next) { bstrncpy(bv->MediaType, lc->str, sizeof(bv->MediaType)); } return bsr; } /* Shove the Device name in each Volume in the current bsr */ static BSR *store_device(LEX *lc, BSR *bsr) { int token; token = lex_get_token(lc, T_STRING); if (token == T_ERROR) { return NULL; } if (!bsr->volume) { Emsg1(M_ERROR,0, _("Device \"%s\" in bsr at inappropriate place.\n"), lc->str); return bsr; } BSR_VOLUME *bv; for (bv=bsr->volume; bv; bv=bv->next) { bstrncpy(bv->device, lc->str, sizeof(bv->device)); } return bsr; } static BSR *store_client(LEX *lc, BSR *bsr) { int token; BSR_CLIENT *client; for (;;) { token = lex_get_token(lc, T_NAME); if (token == T_ERROR) { return NULL; } client = (BSR_CLIENT *)malloc(sizeof(BSR_CLIENT)); memset(client, 0, sizeof(BSR_CLIENT)); bstrncpy(client->ClientName, lc->str, sizeof(client->ClientName)); /* Add it to the end of the client chain */ if (!bsr->client) { bsr->client = client; } else { BSR_CLIENT *bc = bsr->client; for ( ;bc->next; bc=bc->next) { } bc->next = client; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_job(LEX *lc, BSR *bsr) { int token; BSR_JOB *job; for (;;) { token = lex_get_token(lc, T_NAME); if (token == T_ERROR) { return NULL; } job = (BSR_JOB *)malloc(sizeof(BSR_JOB)); memset(job, 0, sizeof(BSR_JOB)); bstrncpy(job->Job, lc->str, sizeof(job->Job)); /* Add it to the end of the client chain */ if (!bsr->job) { bsr->job = job; } else { /* Add to end of chain */ BSR_JOB *bc = bsr->job; for ( ;bc->next; bc=bc->next) { } bc->next = job; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_findex(LEX *lc, BSR *bsr) { int token; BSR_FINDEX *findex; for (;;) { token = lex_get_token(lc, T_PINT32_RANGE); if (token == T_ERROR) { return NULL; } findex = (BSR_FINDEX *)malloc(sizeof(BSR_FINDEX)); memset(findex, 0, sizeof(BSR_FINDEX)); findex->findex = lc->pint32_val; findex->findex2 = lc->pint32_val2; /* Add it to the end of the chain */ if (!bsr->FileIndex) { bsr->FileIndex = findex; } else { /* Add to end of chain */ BSR_FINDEX *bs = bsr->FileIndex; for ( ;bs->next; bs=bs->next) { } bs->next = findex; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_jobid(LEX *lc, BSR *bsr) { int token; BSR_JOBID *jobid; for (;;) { token = lex_get_token(lc, T_PINT32_RANGE); if (token == T_ERROR) { return NULL; } jobid = (BSR_JOBID *)malloc(sizeof(BSR_JOBID)); memset(jobid, 0, sizeof(BSR_JOBID)); jobid->JobId = lc->pint32_val; jobid->JobId2 = lc->pint32_val2; /* Add it to the end of the chain */ if (!bsr->JobId) { bsr->JobId = jobid; } else { /* Add to end of chain */ BSR_JOBID *bs = bsr->JobId; for ( ;bs->next; bs=bs->next) { } bs->next = jobid; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_count(LEX *lc, BSR *bsr) { int token; token = lex_get_token(lc, T_PINT32); if (token == T_ERROR) { return NULL; } bsr->count = lc->pint32_val; scan_to_eol(lc); return bsr; } static BSR *store_jobtype(LEX *lc, BSR *bsr) { /* *****FIXME****** */ Pmsg0(-1, _("JobType not yet implemented\n")); return bsr; } static BSR *store_joblevel(LEX *lc, BSR *bsr) { /* *****FIXME****** */ Pmsg0(-1, _("JobLevel not yet implemented\n")); return bsr; } /* * Routine to handle Volume start/end file */ static BSR *store_volfile(LEX *lc, BSR *bsr) { int token; BSR_VOLFILE *volfile; for (;;) { token = lex_get_token(lc, T_PINT32_RANGE); if (token == T_ERROR) { return NULL; } volfile = (BSR_VOLFILE *)malloc(sizeof(BSR_VOLFILE)); memset(volfile, 0, sizeof(BSR_VOLFILE)); volfile->sfile = lc->pint32_val; volfile->efile = lc->pint32_val2; /* Add it to the end of the chain */ if (!bsr->volfile) { bsr->volfile = volfile; } else { /* Add to end of chain */ BSR_VOLFILE *bs = bsr->volfile; for ( ;bs->next; bs=bs->next) { } bs->next = volfile; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } /* * Routine to handle Volume start/end Block */ static BSR *store_volblock(LEX *lc, BSR *bsr) { int token; BSR_VOLBLOCK *volblock; for (;;) { token = lex_get_token(lc, T_PINT32_RANGE); if (token == T_ERROR) { return NULL; } volblock = (BSR_VOLBLOCK *)malloc(sizeof(BSR_VOLBLOCK)); memset(volblock, 0, sizeof(BSR_VOLBLOCK)); volblock->sblock = lc->pint32_val; volblock->eblock = lc->pint32_val2; /* Add it to the end of the chain */ if (!bsr->volblock) { bsr->volblock = volblock; } else { /* Add to end of chain */ BSR_VOLBLOCK *bs = bsr->volblock; for ( ;bs->next; bs=bs->next) { } bs->next = volblock; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_sessid(LEX *lc, BSR *bsr) { int token; BSR_SESSID *sid; for (;;) { token = lex_get_token(lc, T_PINT32_RANGE); if (token == T_ERROR) { return NULL; } sid = (BSR_SESSID *)malloc(sizeof(BSR_SESSID)); memset(sid, 0, sizeof(BSR_SESSID)); sid->sessid = lc->pint32_val; sid->sessid2 = lc->pint32_val2; /* Add it to the end of the chain */ if (!bsr->sessid) { bsr->sessid = sid; } else { /* Add to end of chain */ BSR_SESSID *bs = bsr->sessid; for ( ;bs->next; bs=bs->next) { } bs->next = sid; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_sesstime(LEX *lc, BSR *bsr) { int token; BSR_SESSTIME *stime; for (;;) { token = lex_get_token(lc, T_PINT32); if (token == T_ERROR) { return NULL; } stime = (BSR_SESSTIME *)malloc(sizeof(BSR_SESSTIME)); memset(stime, 0, sizeof(BSR_SESSTIME)); stime->sesstime = lc->pint32_val; /* Add it to the end of the chain */ if (!bsr->sesstime) { bsr->sesstime = stime; } else { /* Add to end of chain */ BSR_SESSTIME *bs = bsr->sesstime; for ( ;bs->next; bs=bs->next) { } bs->next = stime; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_stream(LEX *lc, BSR *bsr) { int token; BSR_STREAM *stream; for (;;) { token = lex_get_token(lc, T_INT32); if (token == T_ERROR) { return NULL; } stream = (BSR_STREAM *)malloc(sizeof(BSR_STREAM)); memset(stream, 0, sizeof(BSR_STREAM)); stream->stream = lc->int32_val; /* Add it to the end of the chain */ if (!bsr->stream) { bsr->stream = stream; } else { /* Add to end of chain */ BSR_STREAM *bs = bsr->stream; for ( ;bs->next; bs=bs->next) { } bs->next = stream; } token = lex_get_token(lc, T_ALL); if (token != T_COMMA) { break; } } return bsr; } static BSR *store_slot(LEX *lc, BSR *bsr) { int token; token = lex_get_token(lc, T_PINT32); if (token == T_ERROR) { return NULL; } if (!bsr->volume) { Emsg1(M_ERROR,0, _("Slot %d in bsr at inappropriate place.\n"), lc->pint32_val); return bsr; } bsr->volume->Slot = lc->pint32_val; scan_to_eol(lc); return bsr; } static BSR *store_include(LEX *lc, BSR *bsr) { scan_to_eol(lc); return bsr; } static BSR *store_exclude(LEX *lc, BSR *bsr) { scan_to_eol(lc); return bsr; } void dump_volfile(BSR_VOLFILE *volfile) { if (volfile) { Pmsg2(-1, _("VolFile : %u-%u\n"), volfile->sfile, volfile->efile); dump_volfile(volfile->next); } } void dump_volblock(BSR_VOLBLOCK *volblock) { if (volblock) { Pmsg2(-1, _("VolBlock : %u-%u\n"), volblock->sblock, volblock->eblock); dump_volblock(volblock->next); } } void dump_findex(BSR_FINDEX *FileIndex) { if (FileIndex) { if (FileIndex->findex == FileIndex->findex2) { Pmsg1(-1, _("FileIndex : %u\n"), FileIndex->findex); } else { Pmsg2(-1, _("FileIndex : %u-%u\n"), FileIndex->findex, FileIndex->findex2); } dump_findex(FileIndex->next); } } void dump_jobid(BSR_JOBID *jobid) { if (jobid) { if (jobid->JobId == jobid->JobId2) { Pmsg1(-1, _("JobId : %u\n"), jobid->JobId); } else { Pmsg2(-1, _("JobId : %u-%u\n"), jobid->JobId, jobid->JobId2); } dump_jobid(jobid->next); } } void dump_sessid(BSR_SESSID *sessid) { if (sessid) { if (sessid->sessid == sessid->sessid2) { Pmsg1(-1, _("SessId : %u\n"), sessid->sessid); } else { Pmsg2(-1, _("SessId : %u-%u\n"), sessid->sessid, sessid->sessid2); } dump_sessid(sessid->next); } } void dump_volume(BSR_VOLUME *volume) { if (volume) { Pmsg1(-1, _("VolumeName : %s\n"), volume->VolumeName); Pmsg1(-1, _(" MediaType : %s\n"), volume->MediaType); Pmsg1(-1, _(" Device : %s\n"), volume->device); Pmsg1(-1, _(" Slot : %d\n"), volume->Slot); dump_volume(volume->next); } } void dump_client(BSR_CLIENT *client) { if (client) { Pmsg1(-1, _("Client : %s\n"), client->ClientName); dump_client(client->next); } } void dump_job(BSR_JOB *job) { if (job) { Pmsg1(-1, _("Job : %s\n"), job->Job); dump_job(job->next); } } void dump_sesstime(BSR_SESSTIME *sesstime) { if (sesstime) { Pmsg1(-1, _("SessTime : %u\n"), sesstime->sesstime); dump_sesstime(sesstime->next); } } void dump_bsr(BSR *bsr, bool recurse) { int save_debug = debug_level; debug_level = 1; if (!bsr) { Pmsg0(-1, _("BSR is NULL\n")); debug_level = save_debug; return; } Pmsg1(-1, _("Next : 0x%x\n"), bsr->next); Pmsg1(-1, _("Root bsr : 0x%x\n"), bsr->root); dump_volume(bsr->volume); dump_sessid(bsr->sessid); dump_sesstime(bsr->sesstime); dump_volfile(bsr->volfile); dump_volblock(bsr->volblock); dump_client(bsr->client); dump_jobid(bsr->JobId); dump_job(bsr->job); dump_findex(bsr->FileIndex); if (bsr->count) { Pmsg1(-1, _("count : %u\n"), bsr->count); Pmsg1(-1, _("found : %u\n"), bsr->found); } Pmsg1(-1, _("done : %s\n"), bsr->done?_("yes"):_("no")); Pmsg1(-1, _("positioning : %d\n"), bsr->use_positioning); Pmsg1(-1, _("fast_reject : %d\n"), bsr->use_fast_rejection); if (recurse && bsr->next) { Pmsg0(-1, "\n"); dump_bsr(bsr->next, true); } debug_level = save_debug; } /********************************************************************* * * Free bsr resources */ static void free_bsr_item(BSR *bsr) { if (bsr) { free_bsr_item(bsr->next); free(bsr); } } void free_bsr(BSR *bsr) { if (!bsr) { return; } free_bsr_item((BSR *)bsr->volume); free_bsr_item((BSR *)bsr->client); free_bsr_item((BSR *)bsr->sessid); free_bsr_item((BSR *)bsr->sesstime); free_bsr_item((BSR *)bsr->volfile); free_bsr_item((BSR *)bsr->volblock); free_bsr_item((BSR *)bsr->JobId); free_bsr_item((BSR *)bsr->job); free_bsr_item((BSR *)bsr->FileIndex); free_bsr_item((BSR *)bsr->JobType); free_bsr_item((BSR *)bsr->JobLevel); free_bsr(bsr->next); free(bsr); } /***************************************************************** * Routines for handling volumes */ VOL_LIST *new_restore_volume() { VOL_LIST *vol; vol = (VOL_LIST *)malloc(sizeof(VOL_LIST)); memset(vol, 0, sizeof(VOL_LIST)); return vol; } /* * Add current volume to end of list, only if the Volume * is not already in the list. * * returns: 1 if volume added * 0 if volume already in list */ int add_restore_volume(JCR *jcr, VOL_LIST *vol) { VOL_LIST *next = jcr->VolList; if (!next) { /* list empty ? */ jcr->VolList = vol; /* yes, add volume */ } else { for ( ; next->next; next=next->next) { if (strcmp(vol->VolumeName, next->VolumeName) == 0) { if (vol->start_file < next->start_file) { next->start_file = vol->start_file; } return 0; /* already in list */ } } if (strcmp(vol->VolumeName, next->VolumeName) == 0) { if (vol->start_file < next->start_file) { next->start_file = vol->start_file; } return 0; /* already in list */ } next->next = vol; /* add volume */ } return 1; } void free_restore_volume_list(JCR *jcr) { VOL_LIST *next = jcr->VolList; VOL_LIST *tmp; for ( ; next; ) { tmp = next->next; free(next); next = tmp; } jcr->VolList = NULL; } /* * Create a list of Volumes (and Slots and Start positions) to be * used in the current restore job. */ void create_restore_volume_list(JCR *jcr) { char *p, *n; VOL_LIST *vol; /* * Build a list of volumes to be processed */ jcr->NumReadVolumes = 0; jcr->CurReadVolume = 0; if (jcr->bsr) { BSR *bsr = jcr->bsr; if (!bsr->volume || !bsr->volume->VolumeName) { return; } for ( ; bsr; bsr=bsr->next) { BSR_VOLUME *bsrvol; BSR_VOLFILE *volfile; uint32_t sfile = UINT32_MAX; /* Find minimum start file so that we can forward space to it */ for (volfile = bsr->volfile; volfile; volfile=volfile->next) { if (volfile->sfile < sfile) { sfile = volfile->sfile; } } /* Now add volumes for this bsr */ for (bsrvol = bsr->volume; bsrvol; bsrvol=bsrvol->next) { vol = new_restore_volume(); bstrncpy(vol->VolumeName, bsrvol->VolumeName, sizeof(vol->VolumeName)); bstrncpy(vol->MediaType, bsrvol->MediaType, sizeof(vol->MediaType)); bstrncpy(vol->device, bsrvol->device, sizeof(vol->device)); vol->Slot = bsrvol->Slot; vol->start_file = sfile; if (add_restore_volume(jcr, vol)) { jcr->NumReadVolumes++; Dmsg2(400, "Added volume=%s mediatype=%s\n", vol->VolumeName, vol->MediaType); } else { Dmsg1(400, "Duplicate volume %s\n", vol->VolumeName); free((char *)vol); } sfile = 0; /* start at beginning of second volume */ } } } else { /* This is the old way -- deprecated */ for (p = jcr->dcr->VolumeName; p && *p; ) { n = strchr(p, '|'); /* volume name separator */ if (n) { *n++ = 0; /* Terminate name */ } vol = new_restore_volume(); bstrncpy(vol->VolumeName, p, sizeof(vol->VolumeName)); bstrncpy(vol->MediaType, jcr->dcr->media_type, sizeof(vol->MediaType)); if (add_restore_volume(jcr, vol)) { jcr->NumReadVolumes++; } else { free((char *)vol); } p = n; } } }