X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fbsr.c;h=61de96ddfe77a622ae7ef3e10a3020e5ad7333c0;hb=10cfd798ced2d27f61ead2de6fe9b1bcc8e3468d;hp=dd6f4b0a0e78aabb91297bfbef27dcea9a8fffb3;hpb=7232c33915addb5a96a729e9dd1d09cb22127c60;p=bacula%2Fbacula diff --git a/bacula/src/dird/bsr.c b/bacula/src/dird/bsr.c index dd6f4b0a0e..61de96ddfe 100644 --- a/bacula/src/dird/bsr.c +++ b/bacula/src/dird/bsr.c @@ -1,42 +1,37 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2018 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ /* * * Bacula Director -- Bootstrap Record routines. * - * BSR (bootstrap record) handling routines split from - * ua_restore.c July MMIII + * BSR (bootstrap record) handling routines split from + * ua_restore.c July MMIII * * Kern Sibbald, July MMII * - * Version $Id$ - */ - -/* - Copyright (C) 2002-2005 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - 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., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. - */ #include "bacula.h" #include "dird.h" /* Forward referenced functions */ -static uint32_t write_bsr(UAContext *ua, RBSR *bsr, FILE *fd); -void print_bsr(UAContext *ua, RBSR *bsr); - +static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd); /* * Create new FileIndex entry for BSR @@ -48,13 +43,25 @@ RBSR_FINDEX *new_findex() return fi; } -/* Free all BSR FileIndex entries */ -static void free_findex(RBSR_FINDEX *fi) +/* + * Get storage device name from Storage resource + */ +static bool get_storage_device(char *device, char *storage) { - if (fi) { - free_findex(fi->next); - free(fi); + STORE *store; + if (storage[0] == 0) { + return false; } + store = (STORE *)GetResWithName(R_STORAGE, storage); + if (!store) { + return false; + } + DEVICE *dev = (DEVICE *)(store->device->first()); + if (!dev) { + return false; + } + bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH); + return true; } /* @@ -67,26 +74,58 @@ static void free_findex(RBSR_FINDEX *fi) * We are called here once for each JobMedia record * for each Volume. */ -static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi, - int32_t FirstIndex, int32_t LastIndex, FILE *fd) +static uint32_t write_findex(rblist *fi_list, + int32_t FirstIndex, int32_t LastIndex, FILE *fd) { + RBSR_FINDEX *fi; uint32_t count = 0; - for ( ; fi; fi=fi->next) { + + fi = (RBSR_FINDEX *) fi_list->first(); + while (fi) { int32_t findex, findex2; - if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) || - (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) || - (fi->findex < FirstIndex && fi->findex2 > LastIndex)) { - findex = fi->findex < FirstIndex ? FirstIndex : fi->findex; - findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2; - if (findex == findex2) { + + /* fi points to the first item of the list, or the next item that is not + * contigous to the previous group + */ + findex = fi->findex; + findex2 = fi->findex2; + + /* Sometime (with the restore command for example), the fi_list can + * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop + * is here to merge blocks and reduce the bsr output. The next while(fi) + * iteration will use the next_fi that points to the last merged element. + */ + RBSR_FINDEX *next_fi; + for (next_fi = (RBSR_FINDEX*) fi_list->next(fi); + next_fi && next_fi->findex == (findex2+1); + next_fi = (RBSR_FINDEX *) fi_list->next(next_fi)) + { + findex2 = next_fi->findex2; + } + + /* next_fi points after the current block (or to the end of the list), so + * the next while() iteration will use the next value + */ + fi = next_fi; + + /* We look if the current FI block match the volume information */ + if ((findex >= FirstIndex && findex <= LastIndex) || + (findex2 >= FirstIndex && findex2 <= LastIndex) || + (findex < FirstIndex && findex2 > LastIndex)) { + + findex = findex < FirstIndex ? FirstIndex : findex; + findex2 = findex2 > LastIndex ? LastIndex : findex2; + + if (findex == findex2) { fprintf(fd, "FileIndex=%d\n", findex); - count++; - } else { + count++; + } else { fprintf(fd, "FileIndex=%d-%d\n", findex, findex2); - count += findex2 - findex + 1; - } + count += findex2 - findex + 1; + } } } + return count; } @@ -94,319 +133,433 @@ static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi, * Find out if Volume defined with FirstIndex and LastIndex * falls within the range of selected files in the bsr. */ -static bool is_volume_selected(RBSR_FINDEX *fi, - int32_t FirstIndex, int32_t LastIndex) +static bool is_volume_selected(rblist *fi_list, + int32_t FirstIndex, int32_t LastIndex) { - if (fi) { + RBSR_FINDEX *fi; + foreach_rblist(fi, fi_list) { if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) || - (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) || - (fi->findex < FirstIndex && fi->findex2 > LastIndex)) { - return true; + (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) || + (fi->findex < FirstIndex && fi->findex2 > LastIndex)) { + return true; } - return is_volume_selected(fi->next, FirstIndex, LastIndex); } return false; } - -static void print_findex(UAContext *ua, RBSR_FINDEX *fi) -{ - bsendmsg(ua, "fi=0x%lx\n", fi); - for ( ; fi; fi=fi->next) { - if (fi->findex == fi->findex2) { - bsendmsg(ua, "FileIndex=%d\n", fi->findex); -// Dmsg1(100, "FileIndex=%d\n", fi->findex); - } else { - bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2); -// Dmsg2(100, "FileIndex=%d-%d\n", fi->findex, fi->findex2); - } - } -} - /* Create a new bootstrap record */ RBSR *new_bsr() { + RBSR_FINDEX *fi=NULL; RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR)); memset(bsr, 0, sizeof(RBSR)); + bsr->fi_list = New(rblist(fi, &fi->link)); return bsr; } /* Free the entire BSR */ -void free_bsr(RBSR *bsr) +void free_bsr(rblist *bsr_list) { - if (bsr) { - free_findex(bsr->fi); + RBSR *bsr; + foreach_rblist(bsr, bsr_list) { + delete bsr->fi_list; if (bsr->VolParams) { - free(bsr->VolParams); + free(bsr->VolParams); + } + if (bsr->fileregex) { + free(bsr->fileregex); + } + if (bsr->m_fi) { + free(bsr->m_fi); } - free_bsr(bsr->next); - free(bsr); } + delete bsr_list; } /* * Complete the BSR by filling in the VolumeName and * VolSessionId and VolSessionTime using the JobId */ -int complete_bsr(UAContext *ua, RBSR *bsr) +bool complete_bsr(UAContext *ua, rblist *bsr_list) { - if (bsr) { + RBSR *bsr; + foreach_rblist(bsr, bsr_list) { JOB_DBR jr; memset(&jr, 0, sizeof(jr)); jr.JobId = bsr->JobId; if (!db_get_job_record(ua->jcr, ua->db, &jr)) { - bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db)); - return 0; + ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db)); + return false; } bsr->VolSessionId = jr.VolSessionId; bsr->VolSessionTime = jr.VolSessionTime; + if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */ + bsr->VolCount = 0; /* there are no volumes */ + continue; + } if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId, - &(bsr->VolParams))) == 0) { - bsendmsg(ua, _("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db)); - if (bsr->VolParams) { - free(bsr->VolParams); - bsr->VolParams = NULL; - } - return 0; + &(bsr->VolParams))) == 0) { + ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db)); + if (bsr->VolParams) { + free(bsr->VolParams); + bsr->VolParams = NULL; + } + return false; } - return complete_bsr(ua, bsr->next); } - return 1; + return true; +} + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static uint32_t uniq = 0; + +static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname) +{ + JCR *jcr = ua->jcr; + int i = find_arg_with_value(ua, "bootstrap"); + if (i >= 0) { + Mmsg(fname, "%s", ua->argv[i]); + jcr->unlink_bsr = false; + } else { + P(mutex); + uniq++; + V(mutex); + Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq); + jcr->unlink_bsr = true; + } + if (jcr->RestoreBootstrap) { + free(jcr->RestoreBootstrap); + } + jcr->RestoreBootstrap = bstrdup(fname.c_str()); } /* * Write the bootstrap records to file */ -uint32_t write_bsr_file(UAContext *ua, RBSR *bsr) +uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx) { FILE *fd; - POOLMEM *fname = get_pool_memory(PM_MESSAGE); + POOL_MEM fname(PM_MESSAGE); uint32_t count = 0;; bool err; - Mmsg(fname, "%s/restore.bsr", working_directory); - fd = fopen(fname, "w+"); + make_unique_restore_filename(ua, fname); + fd = bfopen(fname.c_str(), "w+b"); if (!fd) { berrno be; - bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"), - fname, be.strerror()); + ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"), + fname.c_str(), be.bstrerror()); goto bail_out; } /* Write them to file */ - count = write_bsr(ua, bsr, fd); + count = write_bsr(ua, rx, fd); err = ferror(fd); fclose(fd); + if (count == 0) { + ua->info_msg(_("No files found to read. No bootstrap file written.\n")); + goto bail_out; + } if (err) { - bsendmsg(ua, _("Error writing bsr file.\n")); + ua->error_msg(_("Error writing bsr file.\n")); count = 0; goto bail_out; } + if (chk_dbglvl(10)) { + print_bsr(ua, rx); + } - bsendmsg(ua, _("Bootstrap records written to %s\n"), fname); +bail_out: + return count; +} + +static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId) +{ + POOL_MEM volmsg(PM_MESSAGE); + char Device[MAX_NAME_LENGTH]; + char online; + RBSR *bsr; + + foreach_rblist(bsr, rx.bsr_list) { + if (JobId && JobId != bsr->JobId) { + continue; + } + + for (int i=0; i < bsr->VolCount; i++) { + if (bsr->VolParams[i].VolumeName[0]) { + if (!get_storage_device(Device, bsr->VolParams[i].Storage)) { + Device[0] = 0; + } + if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) { + online = '*'; + } else { + online = ' '; + } + Mmsg(volmsg, "%c%-25s %-25s %-25s", + online, bsr->VolParams[i].VolumeName, + bsr->VolParams[i].Storage, Device); + add_prompt(ua, volmsg.c_str()); + } + } + } +} + +void display_bsr_info(UAContext *ua, RESTORE_CTX &rx) +{ + char *p; + JobId_t JobId; /* Tell the user what he will need to mount */ - bsendmsg(ua, "\n"); - bsendmsg(ua, _("The job will require the following Volumes:\n")); + ua->send_msg("\n"); + ua->send_msg(_("The Job will require the following (*=>InChanger):\n" + " Volume(s) Storage(s) SD Device(s)\n" + "===========================================================================\n")); /* Create Unique list of Volumes using prompt list */ start_prompt(ua, ""); - for (RBSR *nbsr=bsr; nbsr; nbsr=nbsr->next) { - for (int i=0; i < nbsr->VolCount; i++) { - if (nbsr->VolParams[i].VolumeName[0]) { - add_prompt(ua, nbsr->VolParams[i].VolumeName); - } + if (*rx.JobIds == 0) { + /* Print Volumes in any order */ + display_vol_info(ua, rx, 0); + } else { + /* Ensure that the volumes are printed in JobId order */ + for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) { + display_vol_info(ua, rx, JobId); } } for (int i=0; i < ua->num_prompts; i++) { - bsendmsg(ua, " %s\n", ua->prompt[i]); + ua->send_msg(" %s\n", ua->prompt[i]); free(ua->prompt[i]); + if (ua->unique[i]) free(ua->unique[i]); } if (ua->num_prompts == 0) { - bsendmsg(ua, _("No Volumes found to restore.\n")); - count = 0; + ua->send_msg(_("No Volumes found to restore.\n")); + } else { + ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n")); } ua->num_prompts = 0; - bsendmsg(ua, "\n"); + ua->send_msg("\n"); -bail_out: - free_pool_memory(fname); - return count; + return; } -static uint32_t write_bsr(UAContext *ua, RBSR *bsr, FILE *fd) +/* + * Write bsr data for a single bsr record + */ +static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua, + RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex) { + char ed1[50], ed2[50]; uint32_t count = 0; uint32_t total_count = 0; - uint32_t LastIndex = 0; - bool first = true; - if (bsr) { - /* - * For a given volume, loop over all the JobMedia records. - * VolCount is the number of JobMedia records. + char device[MAX_NAME_LENGTH]; + + /* + * For a given volume, loop over all the JobMedia records. + * VolCount is the number of JobMedia records. + */ + for (int i=0; i < bsr->VolCount; i++) { + if (!is_volume_selected(bsr->fi_list, bsr->VolParams[i].FirstIndex, + bsr->VolParams[i].LastIndex)) { + bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */ + continue; + } + if (!rx.store) { + find_storage_resource(ua, rx, bsr->VolParams[i].Storage, + bsr->VolParams[i].MediaType); + } + fprintf(fd, "Storage=\"%s\"\n", bsr->VolParams[i].Storage); + fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); + fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType); + if (bsr->fileregex) { + fprintf(fd, "FileRegex=%s\n", bsr->fileregex); + } + if (get_storage_device(device, bsr->VolParams[i].Storage)) { + fprintf(fd, "Device=\"%s\"\n", device); + } + if (bsr->VolParams[i].Slot > 0) { + fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot); + } + fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId); + fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime); + fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1), + edit_uint64(bsr->VolParams[i].EndAddr, ed2)); + Dmsg2(100, "bsr VolParam FI=%u LI=%u\n", + bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex); + + count = write_findex(bsr->fi_list, bsr->VolParams[i].FirstIndex, + bsr->VolParams[i].LastIndex, fd); + if (count) { + fprintf(fd, "Count=%u\n", count); + } + total_count += count; + /* If the same file is present on two tapes or in two files + * on a tape, it is a continuation, and should not be treated + * twice in the totals. */ - for (int i=0; i < bsr->VolCount; i++) { - if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex, - bsr->VolParams[i].LastIndex)) { - bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */ - continue; - } - fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); - fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType); - fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId); - fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime); - 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(100, "bsr VolParam FI=%u LI=%u\n", -// bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex); - - count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex, - bsr->VolParams[i].LastIndex, fd); - if (count) { - fprintf(fd, "Count=%u\n", count); - } - total_count += count; - /* If the same file is present on two tapes or in two files - * on a tape, it is a continuation, and should not be treated - * twice in the totals. - */ - if (!first && LastIndex == bsr->VolParams[i].FirstIndex) { - total_count--; - } - first = false; - LastIndex = bsr->VolParams[i].LastIndex; + if (!first && LastIndex == bsr->VolParams[i].FirstIndex) { + total_count--; } - write_bsr(ua, bsr->next, fd); + first = false; + LastIndex = bsr->VolParams[i].LastIndex; } return total_count; } -void print_bsr(UAContext *ua, RBSR *bsr) + +/* + * Here we actually write out the details of the bsr file. + * Note, there is one bsr for each JobId, but the bsr may + * have multiple volumes, which have been entered in the + * order they were written. + * The bsrs must be written out in the order the JobIds + * are found in the jobid list. + */ +static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd) { - if (bsr) { - for (int i=0; i < bsr->VolCount; i++) { - bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); - bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType); - bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId); - bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime); - bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile, - bsr->VolParams[i].EndFile); - bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock, - bsr->VolParams[i].EndBlock); - print_findex(ua, bsr->fi); + bool first = true; + uint32_t LastIndex = 0; + uint32_t total_count = 0; + char *p; + JobId_t JobId; + RBSR *bsr; + if (*rx.JobIds == 0) { + foreach_rblist(bsr, rx.bsr_list) { + total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex); + } + return total_count; + } + for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) { + foreach_rblist(bsr, rx.bsr_list) { + if (JobId == bsr->JobId) { + total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex); + } } - print_bsr(ua, bsr->next); } + return total_count; +} + +void print_bsr(UAContext *ua, RESTORE_CTX &rx) +{ + write_bsr(ua, rx, stdout); } +static int search_rbsr(void *elt1, void *elt2) +{ + RBSR *bsr1 = (RBSR *)elt1; + RBSR *bsr = (RBSR *)elt2; + + /* We might replace by a simple JobId - JobId */ + if (bsr->JobId == bsr1->JobId) { + return 0; + } else if (bsr->JobId < bsr1->JobId) { + return 1; + } + return -1; +} + +static int search_fi(void *elt1, void *elt2) +{ + RBSR_FINDEX *f1 = (RBSR_FINDEX *) elt1; + RBSR_FINDEX *f2 = (RBSR_FINDEX *) elt2; + + if (f1->findex == (f2->findex - 1)) { + return 0; + + } else if (f1->findex2 == (f2->findex2 + 1)) { + return 0; + + } else if (f1->findex >= f2->findex && f1->findex2 <= f2->findex2) { + return 0; + } + + return (f1->findex > f2->findex) ? 1 : -1; +} + +rblist *create_bsr_list(uint32_t JobId, int findex, int findex2) +{ + RBSR *bsr = NULL; + RBSR_FINDEX *fi = NULL; + rblist *bsr_list = New(rblist(bsr, &bsr->link)); + + bsr = new_bsr(); + bsr->JobId = JobId; + + bsr_list->insert(bsr, search_rbsr); + + fi = new_findex(); + fi->findex = findex; + fi->findex2 = findex2; + + bsr->fi_list->insert(fi, search_fi); + + return bsr_list; +} /* * Add a FileIndex to the list of BootStrap records. * Here we are only dealing with JobId's and the FileIndexes * associated with those JobIds. + * We expect that JobId, FileIndex are sorted ascending. + * + * When doing restore from tree, FileIndex are not sorted, so it can + * create gaps. */ -void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) +void add_findex(rblist *bsr_list, uint32_t JobId, int32_t findex) { - RBSR *nbsr; - RBSR_FINDEX *fi, *lfi; + RBSR *bsr, bsr2; + RBSR_FINDEX *fi, *nfi; if (findex == 0) { - return; /* probably a dummy directory */ + return; /* probably a dummy directory */ } - if (bsr->fi == NULL) { /* if no FI add one */ - /* This is the first FileIndex item in the chain */ - bsr->fi = new_findex(); - bsr->JobId = JobId; - bsr->fi->findex = findex; - bsr->fi->findex2 = findex; - return; - } + bsr2.JobId = JobId; /* Walk down list of bsrs until we find the JobId */ - if (bsr->JobId != JobId) { - for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) { - if (nbsr->JobId == JobId) { - bsr = nbsr; - break; - } - } + bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr); - if (!nbsr) { /* Must add new JobId */ - /* Add new JobId at end of chain */ - for (nbsr=bsr; nbsr->next; nbsr=nbsr->next) - { } - nbsr->next = new_bsr(); - nbsr->next->JobId = JobId; - nbsr->next->fi = new_findex(); - nbsr->next->fi->findex = findex; - nbsr->next->fi->findex2 = findex; - return; - } + /* The list is empty, or the JobId is not already in, + * Must add new JobId + */ + if (!bsr) { + bsr = new_bsr(); + bsr->JobId = JobId; + bsr_list->insert(bsr, search_rbsr); } - /* - * At this point, bsr points to bsr containing this JobId, - * and we are sure that there is at least one fi record. - */ - lfi = fi = bsr->fi; - /* Check if this findex is smaller than first item */ - if (findex < fi->findex) { - if ((findex+1) == fi->findex) { - fi->findex = findex; /* extend down */ - return; - } - fi = new_findex(); /* yes, insert before first item */ - fi->findex = findex; - fi->findex2 = findex; - fi->next = lfi; - bsr->fi = fi; - return; + if (bsr->m_fi) { + fi = bsr->m_fi; + + } else { + fi = bsr->m_fi = new_findex(); } - /* Walk down fi chain and find where to insert insert new FileIndex */ - for ( ; fi; fi=fi->next) { - if (findex == (fi->findex2 + 1)) { /* extend up */ - RBSR_FINDEX *nfi; - fi->findex2 = findex; - /* - * If the following record contains one higher, merge its - * file index by extending it up. - */ - if (fi->next && ((findex+1) == fi->next->findex)) { - nfi = fi->next; - fi->findex2 = nfi->findex2; - fi->next = nfi->next; - free(nfi); - } - return; - } - if (findex < fi->findex) { /* add before */ - if ((findex+1) == fi->findex) { - fi->findex = findex; - return; - } - break; + + fi->findex = findex; + fi->findex2 = findex; + + Dmsg1(1000, "Trying to insert %ld\n", findex); + /* try to insert our fi */ + nfi = (RBSR_FINDEX*) bsr->fi_list->insert((void *)fi, search_fi); + + /* We found an existing one, extend it */ + if (nfi != fi) { + if (findex == (nfi->findex2 + 1)) { + Dmsg2(1000, "Extend %ld-%ld\n", nfi->findex, findex); + nfi->findex2 = findex; + + } else if (findex == (nfi->findex - 1)) { + Dmsg2(1000, "Extend %ld-%ld\n", findex, nfi->findex2); + nfi->findex = findex; + + } else { + Dmsg2(1000, "Found the same values? %ld-%ld\n", nfi->findex, nfi->findex2); } - lfi = fi; + + } else { + Dmsg2(1000, "Inserted %ld-%ld\n", fi->findex, fi->findex2); + bsr->m_fi = NULL; /* comsumed */ } - /* Add to last place found */ - fi = new_findex(); - fi->findex = findex; - fi->findex2 = findex; - fi->next = lfi->next; - lfi->next = fi; - return; } /* @@ -414,48 +567,194 @@ void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) * Here we are only dealing with JobId's and the FileIndexes * associated with those JobIds. */ -void add_findex_all(RBSR *bsr, uint32_t JobId) +void add_findex_all(rblist *bsr_list, uint32_t JobId, const char *fileregex) { - RBSR *nbsr; + RBSR *bsr, bsr2; RBSR_FINDEX *fi; - if (bsr->fi == NULL) { /* if no FI add one */ - /* This is the first FileIndex item in the chain */ - bsr->fi = new_findex(); - bsr->JobId = JobId; - bsr->fi->findex = 1; - bsr->fi->findex2 = INT32_MAX; - return; - } + bsr2.JobId = JobId; /* Walk down list of bsrs until we find the JobId */ - if (bsr->JobId != JobId) { - for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) { - if (nbsr->JobId == JobId) { - bsr = nbsr; - break; - } - } + bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr); + + if (!bsr) { /* Must add new JobId */ + fi = new_findex(); + fi->findex = 1; + fi->findex2 = INT32_MAX; - if (!nbsr) { /* Must add new JobId */ - /* Add new JobId at end of chain */ - for (nbsr=bsr; nbsr->next; nbsr=nbsr->next) - { } - nbsr->next = new_bsr(); - nbsr->next->JobId = JobId; - nbsr->next->fi = new_findex(); - nbsr->next->fi->findex = 1; - nbsr->next->fi->findex2 = INT32_MAX; - return; + bsr = new_bsr(); + bsr->JobId = JobId; + bsr->fi_list->insert(fi, search_fi); + bsr_list->insert(bsr, search_rbsr); + + if (fileregex) { + /* If we use regexp to restore, set it for each jobid */ + bsr->fileregex = bstrdup(fileregex); } + return; } /* * At this point, bsr points to bsr containing this JobId, - * and we are sure that there is at least one fi record. */ - fi = bsr->fi; + fi = new_findex(); fi->findex = 1; fi->findex2 = INT32_MAX; + bsr->fi_list->insert(fi, search_fi); return; } +#ifdef needed +/* Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD + * row[0]=Path, row[1]=Filename, row[2]=FileIndex + * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5 + */ +static int sendit(void *arg, int num_fields, char **row) +{ + JCR *jcr = (JCR *)arg; + + if (job_canceled(jcr)) { + return 1; + } + + if (row[2][0] == '0') { /* discard when file_index == 0 */ + return 0; + } + + /* sending with checksum */ + if (num_fields == 7 + && row[6][0] /* skip checksum = '0' */ + && row[6][1]) + { + jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s", + row[0], row[1], 0, row[4], 0, row[6], 0, row[5]); + } else { + jcr->file_bsock->fsend("%s%s%c%s%c%c%s", + row[0], row[1], 0, row[4], 0, 0, row[5]); + } + return 0; +} +#endif + +/* We list all files for a given FI structure */ +static void scan_findex(JCR *jcr, RBSR *bsr, + int32_t FirstIndex, int32_t LastIndex, + int32_t &lastFileIndex, uint32_t &lastJobId) +{ + RBSR_FINDEX *fi; + FILE_DBR fdbr; + memset(&fdbr, 0, sizeof(fdbr)); + + fi = (RBSR_FINDEX *) bsr->fi_list->first(); + while (fi) { + int32_t findex, findex2; + + /* fi points to the first item of the list, or the next item that is not + * contigous to the previous group + */ + findex = fi->findex; + findex2 = fi->findex2; + + /* Sometime (with the restore command for example), the fi_list can + * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop + * is here to merge blocks and reduce the bsr output. The next while(fi) + * iteration will use the next_fi that points to the last merged element. + */ + RBSR_FINDEX *next_fi; + for (next_fi = (RBSR_FINDEX*) bsr->fi_list->next(fi); + next_fi && next_fi->findex == (findex2+1); + next_fi = (RBSR_FINDEX *) bsr->fi_list->next(next_fi)) + { + findex2 = next_fi->findex2; + } + + /* next_fi points after the current block (or to the end of the list), so + * the next while() iteration will use the next value + */ + fi = next_fi; + + /* We look if the current FI block match the volume information */ + if ((findex >= FirstIndex && findex <= LastIndex) || + (findex2 >= FirstIndex && findex2 <= LastIndex) || + (findex < FirstIndex && findex2 > LastIndex)) { + + findex = findex < FirstIndex ? FirstIndex : findex; + findex2 = findex2 > LastIndex ? LastIndex : findex2; + + bool dolist=false; + /* Display only new files */ + if (findex != lastFileIndex || bsr->JobId != lastJobId) { + /* Not the same file, or not the same job */ + fdbr.FileIndex = findex; + //dolist = true; + + } else if (findex2 != lastFileIndex) { + /* We are in the same job, and the first index was already generated */ + fdbr.FileIndex = findex + 1; + //dolist = true; + } + + /* Keep the current values for the next loop */ + lastJobId = bsr->JobId; + lastFileIndex = findex2; + + /* Generate if needed the list of files */ + if (dolist) { + fdbr.FileIndex2 = findex2; + fdbr.JobId = bsr->JobId; + /* New code not working */ + //db_list_files(jcr, jcr->db, &fdbr, sendit, jcr); + } + } + } +} + +/* + * Scan bsr data for a single bsr record + */ +static void scan_bsr_item(JCR *jcr, RBSR *bsr) +{ + int32_t lastFileIndex=0; + uint32_t lastJobId=0; + /* + * For a given volume, loop over all the JobMedia records. + * VolCount is the number of JobMedia records. + */ + for (int i=0; i < bsr->VolCount; i++) { + if (!is_volume_selected(bsr->fi_list, + bsr->VolParams[i].FirstIndex, + bsr->VolParams[i].LastIndex)) + { + continue; + } + + scan_findex(jcr, bsr, + bsr->VolParams[i].FirstIndex, + bsr->VolParams[i].LastIndex, + lastFileIndex, lastJobId); + } +} + +/* + * We need to find all files from the BSR. All files are listed, this is used + * to send the list of the files to be restored to a plugin for example. + */ +void scan_bsr(JCR *jcr) +{ + char *p; + JobId_t JobId; + RBSR *bsr; + if (!jcr->JobIds || *jcr->JobIds == 0) { + foreach_rblist(bsr, jcr->bsr_list) { + scan_bsr_item(jcr, bsr); + } + return; + } + for (p=jcr->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) { + foreach_rblist(bsr, jcr->bsr_list) { + if (JobId == bsr->JobId) { + scan_bsr_item(jcr, bsr); + } + } + } + return; +}