From 7bf306ad4f5bc3cdeac8b7935abb85846e5528cf Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Mon, 2 Jun 2003 14:26:38 +0000 Subject: [PATCH] Separate tree and bsr code from ua_restore.c git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@560 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/kernstodo | 9 +- bacula/src/cats/sql_find.c | 10 +- bacula/src/dird/Makefile.in | 8 +- bacula/src/dird/bsr.c | 321 +++++++++++++++ bacula/src/dird/bsr.h | 57 +++ bacula/src/dird/dird.h | 1 + bacula/src/dird/job.c | 2 +- bacula/src/dird/protos.h | 12 + bacula/src/dird/ua.h | 40 +- bacula/src/dird/ua_restore.c | 737 +---------------------------------- bacula/src/dird/ua_tree.c | 428 ++++++++++++++++++++ bacula/src/version.h | 4 +- 12 files changed, 871 insertions(+), 758 deletions(-) create mode 100644 bacula/src/dird/bsr.c create mode 100644 bacula/src/dird/bsr.h create mode 100644 bacula/src/dird/ua_tree.c diff --git a/bacula/kernstodo b/bacula/kernstodo index 332e04e12a..79bfbfcc7b 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -32,6 +32,10 @@ Testing to do: (painful) For 1.31 release: - Implement FileSet VolIndex. - Sort JobIds entered into recover tree. +- The bsr for Dan's job has file indexes covering the whole range rather + than only the range contained on the volume. + Constrain FileIndex to be within range for Volume. +- Fix Verify VolumeToCatalog to use BSRs -- it is broken. - Should Bacula make an Append tape as Purged when purging? - Test a second language e.g. french. - Start working on Base jobs. @@ -40,10 +44,6 @@ For 1.31 release: - Unsaved Flag in Job record. - Base Flag in Job record. - Implement UnsavedFiles DB record. -- The bsr for Dan's job has file indexes covering the whole range rather - than only the range contained on the volume. - Constrain FileIndex to be within range for Volume. -- Fix Verify VolumeToCatalog to use BSRs -- it is broken. - Use switch() in backup.c and restore.c in FD instead of giant if statement. - Implement argc/argv for daemon command line scanning using table driven stuff below. @@ -915,4 +915,3 @@ Done: (see kernsdone for more) - Bytes restored is wrong. - The "List last 20 Jobs run" doesnt work correctly in restore. It doesnt show the last 20 jobs , but some older ones. - diff --git a/bacula/src/cats/sql_find.c b/bacula/src/cats/sql_find.c index 8ed28dd9b6..0dab06bcce 100644 --- a/bacula/src/cats/sql_find.c +++ b/bacula/src/cats/sql_find.c @@ -152,14 +152,14 @@ db_find_last_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr) db_lock(mdb); if (jr->Level == L_VERIFY_CATALOG) { Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE Type='%c' AND Level='%c' AND Name='%s' AND " +"SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND Name='%s' AND " "ClientId=%u ORDER BY StartTime DESC LIMIT 1", - JT_VERIFY, L_VERIFY_INIT, jr->Name, jr->ClientId); + L_VERIFY_INIT, jr->Name, jr->ClientId); } else if (jr->Level == L_VERIFY_VOLUME_TO_CATALOG) { Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE Type='%c' AND " -"ClientId=%u ORDER BY StartTime DESC LIMIT 1", - JT_BACKUP, jr->ClientId); +"SELECT JobId FROM Job WHERE Type='B' AND " +"ClientId=%u ORDER BY StartTime DESC LIMIT 1", + jr->ClientId); } else { Mmsg1(&mdb->errmsg, _("Unknown Job level=%c\n"), jr->Level); db_unlock(mdb); diff --git a/bacula/src/dird/Makefile.in b/bacula/src/dird/Makefile.in index bb8a01711a..313a192358 100644 --- a/bacula/src/dird/Makefile.in +++ b/bacula/src/dird/Makefile.in @@ -23,7 +23,7 @@ dummy: # SVRSRCS = dird.c admin.c authenticate.c \ - autoprune.c backup.c \ + autoprune.c backup.c bsr.c \ catreq.c dird_conf.c \ fd_cmds.c getmsg.c inc_conf.c job.c \ mountreq.c msgchan.c newvol.c \ @@ -34,9 +34,9 @@ SVRSRCS = dird.c admin.c authenticate.c \ ua_input.c ua_label.c ua_output.c ua_prune.c \ ua_purge.c ua_restore.c ua_run.c \ ua_select.c ua_server.c \ - ua_status.c verify.c + ua_status.c ua_tree.c verify.c SVROBJS = dird.o admin.o authenticate.o \ - autoprune.o backup.o \ + autoprune.o backup.o bsr.o \ catreq.o dird_conf.o \ fd_cmds.o getmsg.o inc_conf.o job.o \ mountreq.o msgchan.o newvol.o \ @@ -47,7 +47,7 @@ SVROBJS = dird.o admin.o authenticate.o \ ua_input.o ua_label.o ua_output.o ua_prune.o \ ua_purge.o ua_restore.o ua_run.o \ ua_select.o ua_server.o \ - ua_status.o verify.o + ua_status.o ua_tree.o verify.o # these are the objects that are changed by the .configure process EXTRAOBJS = @OBJLIST@ diff --git a/bacula/src/dird/bsr.c b/bacula/src/dird/bsr.c new file mode 100644 index 0000000000..38aa67d2ee --- /dev/null +++ b/bacula/src/dird/bsr.c @@ -0,0 +1,321 @@ +/* + * + * Bacula Director -- Bootstrap Record routines. + * + * BSR (bootstrap record) handling routines split from + * ua_restore.c July MMIII + * + * Kern Sibbald, July MMII + * + * Version $Id$ + */ + +/* + Copyright (C) 2002-2003 Kern Sibbald and John Walker + + 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 RBSR *sort_bsr(RBSR *bsr); +static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd); + + +/* + * Create new FileIndex entry for BSR + */ +static RBSR_FINDEX *new_findex() +{ + RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX)); + memset(fi, 0, sizeof(RBSR_FINDEX)); + return fi; +} + +/* Free all BSR FileIndex entries */ +static void free_findex(RBSR_FINDEX *fi) +{ + if (fi) { + free_findex(fi->next); + free(fi); + } +} + +static void write_findex(UAContext *ua, RBSR_FINDEX *fi, FILE *fd) +{ + if (fi) { + if (fi->findex == fi->findex2) { + fprintf(fd, "FileIndex=%d\n", fi->findex); + } else { + fprintf(fd, "FileIndex=%d-%d\n", fi->findex, fi->findex2); + } + write_findex(ua, fi->next, fd); + } +} + + +static void print_findex(UAContext *ua, RBSR_FINDEX *fi) +{ + if (fi) { + if (fi->findex == fi->findex2) { + bsendmsg(ua, "FileIndex=%d\n", fi->findex); + } else { + bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2); + } + print_findex(ua, fi->next); + } +} + +/* Create a new bootstrap record */ +RBSR *new_bsr() +{ + RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR)); + memset(bsr, 0, sizeof(RBSR)); + return bsr; +} + +/* Free the entire BSR */ +void free_bsr(RBSR *bsr) +{ + if (bsr) { + free_findex(bsr->fi); + free_bsr(bsr->next); + if (bsr->VolParams) { + free(bsr->VolParams); + } + free(bsr); + } +} + +/* + * Complete the BSR by filling in the VolumeName and + * VolSessionId and VolSessionTime using the JobId + */ +int complete_bsr(UAContext *ua, RBSR *bsr) +{ + JOB_DBR jr; + + if (bsr) { + 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; + } + bsr->VolSessionId = jr.VolSessionId; + bsr->VolSessionTime = jr.VolSessionTime; + 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; + } + return complete_bsr(ua, bsr->next); + } + return 1; +} + +/* + * Write the bootstrap record to file + */ +int write_bsr_file(UAContext *ua, RBSR *bsr) +{ + FILE *fd; + POOLMEM *fname = get_pool_memory(PM_MESSAGE); + int stat; + + Mmsg(&fname, "%s/restore.bsr", working_directory); + fd = fopen(fname, "w+"); + if (!fd) { + bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"), + fname, strerror(errno)); + free_pool_memory(fname); + return 0; + } + /* Sort the bsr chain */ + bsr = sort_bsr(bsr); + /* Write them to file */ + write_bsr(ua, bsr, fd); + stat = !ferror(fd); + fclose(fd); + bsendmsg(ua, _("Bootstrap records written to %s\n"), fname); + + /* Tell the user what he will need to mount */ + bsendmsg(ua, _("\nThe restore job will require the following Volumes:\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++) { + add_prompt(ua, nbsr->VolParams[i].VolumeName); + } + } + for (int i=0; i < ua->num_prompts; i++) { + bsendmsg(ua, " %s\n", ua->prompt[i]); + free(ua->prompt[i]); + } + ua->num_prompts = 0; + bsendmsg(ua, "\n"); + free_pool_memory(fname); + return stat; +} + +/* + * First sort the bsr chain, then sort the VolParams + */ +static RBSR *sort_bsr(RBSR *bsr) +{ + if (!bsr) { + return bsr; + } + /* ****FIXME**** sort the bsr chain */ + for (RBSR *nbsr=bsr; nbsr; nbsr=nbsr->next) { + } + return bsr; +} + +static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd) +{ + if (bsr) { + for (int i=0; i < bsr->VolCount; i++) { + 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); + write_findex(ua, bsr->fi, fd); + } + write_bsr(ua, bsr->next, fd); + } +} + +static void print_bsr(UAContext *ua, RBSR *bsr) +{ + if (bsr) { + for (int i=0; i < bsr->VolCount; i++) { + bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); + 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); + } + print_bsr(ua, bsr->next); + } +} + + +/* + * Add a FileIndex to the list of BootStrap records. + * Here we are only dealing with JobId's and the FileIndexes + * associated with those JobIds. + */ +void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) +{ + RBSR *nbsr; + RBSR_FINDEX *fi, *lfi; + + if (findex == 0) { + return; /* probably a dummy directory */ + } + + if (!bsr->fi) { /* 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; + } + /* 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; + } + } + + 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; + } + } + + /* + * At this point, bsr points to bsr containing 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; + } + /* 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 (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; + } + lfi = fi; + } + /* Add to last place found */ + fi = new_findex(); + fi->findex = findex; + fi->findex2 = findex; + fi->next = lfi->next; + lfi->next = fi; + return; +} diff --git a/bacula/src/dird/bsr.h b/bacula/src/dird/bsr.h new file mode 100644 index 0000000000..f133d3ed5a --- /dev/null +++ b/bacula/src/dird/bsr.h @@ -0,0 +1,57 @@ +/* + * + * Bootstrap Record header file + * + * BSR (bootstrap record) handling routines split from + * ua_restore.c July MMIII + * + * Kern Sibbald, July MMII + * + * Version $Id$ + */ + +/* + Copyright (C) 2002-2003 Kern Sibbald and John Walker + + 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. + + */ + + +/* FileIndex entry in restore bootstrap record */ +struct RBSR_FINDEX { + RBSR_FINDEX *next; + int32_t findex; + int32_t findex2; +}; + +/* + * Restore bootstrap record -- not the real one, but useful here + * The restore bsr is a chain of BSR records (linked by next). + * Each BSR represents a single JobId, and within it, it + * contains a linked list of file indexes for that JobId. + * The complete_bsr() routine, will then add all the volumes + * on which the Job is stored to the BSR. + */ +struct RBSR { + RBSR *next; /* next JobId */ + uint32_t JobId; /* JobId this bsr */ + uint32_t VolSessionId; + uint32_t VolSessionTime; + int VolCount; /* Volume parameter count */ + VOL_PARAMS *VolParams; /* Volume, start/end file/blocks */ + RBSR_FINDEX *fi; /* File indexes this JobId */ +}; diff --git a/bacula/src/dird/dird.h b/bacula/src/dird/dird.h index efc225b689..ec6f1ebb68 100644 --- a/bacula/src/dird/dird.h +++ b/bacula/src/dird/dird.h @@ -39,6 +39,7 @@ #include "jcr.h" +#include "bsr.h" #include "ua.h" #include "protos.h" diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index c6172103ca..23f2b98a4c 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -462,7 +462,7 @@ int get_or_create_client_record(JCR *jcr) free_pool_memory(jcr->client_uname); } jcr->client_uname = get_memory(strlen(cr.Uname) + 1); - strcpy(jcr->client_uname, cr.Uname); + pm_strcpy(&jcr->client_uname, cr.Uname); } Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name, jcr->jr.ClientId); diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index 0bc68b9939..f65d8c8671 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -41,6 +41,14 @@ extern int find_recycled_volume(JCR *jcr, MEDIA_DBR *mr); /* backup.c */ extern int wait_for_job_termination(JCR *jcr); +/* bsr.c */ +RBSR *new_bsr(); +void free_bsr(RBSR *bsr); +int complete_bsr(UAContext *ua, RBSR *bsr); +int write_bsr_file(UAContext *ua, RBSR *bsr); +void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex); + + /* catreq.c */ extern void catalog_request(JCR *jcr, BSOCK *bs, char *buf); extern void catalog_update(JCR *jcr, BSOCK *bs, char *buf); @@ -143,6 +151,10 @@ int find_arg_with_value(UAContext *ua, char *keyword); int do_keyword_prompt(UAContext *ua, char *msg, char **list); int confirm_retention(UAContext *ua, utime_t *ret, char *msg); +/* ua_tree.c */ +void user_select_files_from_tree(TREE_CTX *tree); +int insert_tree_handler(void *ctx, int num_fields, char **row); + /* ua_prune.c */ int prune_files(UAContext *ua, CLIENT *client); int prune_jobs(UAContext *ua, CLIENT *client, int JobType); diff --git a/bacula/src/dird/ua.h b/bacula/src/dird/ua.h index f46992802b..d63e949f90 100644 --- a/bacula/src/dird/ua.h +++ b/bacula/src/dird/ua.h @@ -35,21 +35,31 @@ struct UAContext { JCR *jcr; B_DB *db; CAT *catalog; - POOLMEM *cmd; /* return command/name buffer */ - POOLMEM *args; /* command line arguments */ - char *argk[MAX_CMD_ARGS]; /* argument keywords */ - char *argv[MAX_CMD_ARGS]; /* argument values */ - int argc; /* number of arguments */ - char **prompt; /* list of prompts */ - int max_prompts; /* max size of list */ - int num_prompts; /* current number in list */ - int auto_display_messages; /* if set, display messages */ + POOLMEM *cmd; /* return command/name buffer */ + POOLMEM *args; /* command line arguments */ + char *argk[MAX_CMD_ARGS]; /* argument keywords */ + char *argv[MAX_CMD_ARGS]; /* argument values */ + int argc; /* number of arguments */ + char **prompt; /* list of prompts */ + int max_prompts; /* max size of list */ + int num_prompts; /* current number in list */ + int auto_display_messages; /* if set, display messages */ int user_notified_msg_pending; /* set when user notified */ - int automount; /* if set, mount after label */ - int quit; /* if set, quit */ - int verbose; /* set for normal UA verbosity */ - uint32_t pint32_val; /* positive integer */ - int32_t int32_val; /* positive/negative */ -}; + int automount; /* if set, mount after label */ + int quit; /* if set, quit */ + int verbose; /* set for normal UA verbosity */ + uint32_t pint32_val; /* positive integer */ + int32_t int32_val; /* positive/negative */ +}; + +/* Context for insert_tree_handler() */ +struct TREE_CTX { + TREE_ROOT *root; /* root */ + TREE_NODE *node; /* current node */ + TREE_NODE *avail_node; /* unused node last insert */ + int cnt; /* count for user feedback */ + UAContext *ua; +}; + #endif diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 40428f5512..7fae1160ab 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -1,7 +1,12 @@ /* * * Bacula Director -- User Agent Database restore Command - * Creates a bootstrap file for restoring files + * Creates a bootstrap file for restoring files and + * starts the restore job. + * + * Tree handling routines split into ua_tree.c July MMIII. + * BSR (bootstrap record) handling routines split into + * bsr.c July MMIII * * Kern Sibbald, July MMII * @@ -30,9 +35,6 @@ #include "bacula.h" #include "dird.h" -#include -#include "findlib/find.h" - /* Imported functions */ @@ -46,15 +48,6 @@ extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp; extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype; -/* Context for insert_tree_handler() */ -struct TREE_CTX { - TREE_ROOT *root; /* root */ - TREE_NODE *node; /* current node */ - TREE_NODE *avail_node; /* unused node last insert */ - int cnt; /* count for user feedback */ - UAContext *ua; -}; - /* Main structure for obtaining JobIds */ struct JOBIDS { utime_t JobTDate; @@ -64,32 +57,6 @@ struct JOBIDS { STORE *store; }; - -/* FileIndex entry in restore bootstrap record */ -struct RBSR_FINDEX { - RBSR_FINDEX *next; - int32_t findex; - int32_t findex2; -}; - -/* - * Restore bootstrap record -- not the real one, but useful here - * The restore bsr is a chain of BSR records (linked by next). - * Each BSR represents a single JobId, and within it, it - * contains a linked list of file indexes for that JobId. - * The complete_bsr() routine, will then add all the volumes - * on which the Job is stored to the BSR. - */ -struct RBSR { - RBSR *next; /* next JobId */ - uint32_t JobId; /* JobId this bsr */ - uint32_t VolSessionId; - uint32_t VolSessionTime; - int VolCount; /* Volume parameter count */ - VOL_PARAMS *VolParams; /* Volume, start/end file/blocks */ - RBSR_FINDEX *fi; /* File indexes this JobId */ -}; - struct NAME_LIST { char **name; /* list of names */ int num_ids; /* ids stored */ @@ -102,25 +69,15 @@ struct NAME_LIST { /* Forward referenced functions */ -static RBSR *new_bsr(); -static void free_bsr(RBSR *bsr); -static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd); -static int write_bsr_file(UAContext *ua, RBSR *bsr); -static void print_bsr(UAContext *ua, RBSR *bsr); -static int complete_bsr(UAContext *ua, RBSR *bsr); -static int insert_tree_handler(void *ctx, int num_fields, char **row); -static void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex); static int last_full_handler(void *ctx, int num_fields, char **row); static int jobid_handler(void *ctx, int num_fields, char **row); static int next_jobid_from_list(char **p, uint32_t *JobId); static int user_select_jobids(UAContext *ua, JOBIDS *ji); -static void user_select_files(TREE_CTX *tree); static int fileset_handler(void *ctx, int num_fields, char **row); static void print_name_list(UAContext *ua, NAME_LIST *name_list); static int unique_name_list_handler(void *ctx, int num_fields, char **row); static void free_name_list(NAME_LIST *name_list); static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBIDS *ji); -static RBSR *sort_bsr(RBSR *bsr); /* @@ -220,7 +177,8 @@ int restorecmd(UAContext *ua, char *cmd) } } - bsendmsg(ua, "%d items inserted into the tree and marked for extraction.\n", items); + bsendmsg(ua, "%d item%s inserted into the tree and marked for extraction.\n", + items, items==1?"":"s"); free_pool_memory(query); /* Check MediaType and select storage that corresponds */ @@ -228,7 +186,7 @@ int restorecmd(UAContext *ua, char *cmd) free_name_list(&name_list); /* Let the user select which files to restore */ - user_select_files(&tree); + user_select_files_from_tree(&tree); /* * Walk down through the tree finding all files marked to be @@ -544,684 +502,11 @@ static int fileset_handler(void *ctx, int num_fields, char **row) return 0; } -/* Forward referenced commands */ - -static int markcmd(UAContext *ua, TREE_CTX *tree); -static int countcmd(UAContext *ua, TREE_CTX *tree); -static int findcmd(UAContext *ua, TREE_CTX *tree); -static int lscmd(UAContext *ua, TREE_CTX *tree); -static int dircmd(UAContext *ua, TREE_CTX *tree); -static int helpcmd(UAContext *ua, TREE_CTX *tree); -static int cdcmd(UAContext *ua, TREE_CTX *tree); -static int pwdcmd(UAContext *ua, TREE_CTX *tree); -static int unmarkcmd(UAContext *ua, TREE_CTX *tree); -static int quitcmd(UAContext *ua, TREE_CTX *tree); - - -struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; }; -static struct cmdstruct commands[] = { - { N_("mark"), markcmd, _("mark file for restoration")}, - { N_("unmark"), unmarkcmd, _("unmark file for restoration")}, - { N_("cd"), cdcmd, _("change current directory")}, - { N_("pwd"), pwdcmd, _("print current working directory")}, - { N_("ls"), lscmd, _("list current directory")}, - { N_("dir"), dircmd, _("list current directory")}, - { N_("count"), countcmd, _("count marked files")}, - { N_("find"), findcmd, _("find files")}, - { N_("done"), quitcmd, _("leave file selection mode")}, - { N_("exit"), quitcmd, _("exit = done")}, - { N_("help"), helpcmd, _("print help")}, - { N_("?"), helpcmd, _("print help")}, - }; -#define comsize (sizeof(commands)/sizeof(struct cmdstruct)) - - -/* - * Enter a prompt mode where the user can select/deselect - * files to be restored. This is sort of like a mini-shell - * that allows "cd", "pwd", "add", "rm", ... - */ -static void user_select_files(TREE_CTX *tree) -{ - char cwd[2000]; - - bsendmsg(tree->ua, _( - "\nYou are now entering file selection mode where you add and\n" - "remove files to be restored. All files are initially added.\n" - "Enter \"done\" to leave this mode.\n\n")); - /* - * Enter interactive command handler allowing selection - * of individual files. - */ - tree->node = (TREE_NODE *)tree->root; - tree_getpath(tree->node, cwd, sizeof(cwd)); - bsendmsg(tree->ua, _("cwd is: %s\n"), cwd); - for ( ;; ) { - int found, len, stat, i; - if (!get_cmd(tree->ua, "$ ")) { - break; - } - parse_ua_args(tree->ua); - if (tree->ua->argc == 0) { - return; - } - - len = strlen(tree->ua->argk[0]); - found = 0; - stat = 0; - for (i=0; i<(int)comsize; i++) /* search for command */ - if (strncasecmp(tree->ua->argk[0], _(commands[i].key), len) == 0) { - stat = (*commands[i].func)(tree->ua, tree); /* go execute command */ - found = 1; - break; - } - if (!found) { - bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n")); - continue; - } - if (!stat) { - break; - } - } -} - -/* - * Create new FileIndex entry for BSR - */ -static RBSR_FINDEX *new_findex() -{ - RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX)); - memset(fi, 0, sizeof(RBSR_FINDEX)); - return fi; -} - -/* Free all BSR FileIndex entries */ -static void free_findex(RBSR_FINDEX *fi) -{ - if (fi) { - free_findex(fi->next); - free(fi); - } -} - -static void write_findex(UAContext *ua, RBSR_FINDEX *fi, FILE *fd) -{ - if (fi) { - if (fi->findex == fi->findex2) { - fprintf(fd, "FileIndex=%d\n", fi->findex); - } else { - fprintf(fd, "FileIndex=%d-%d\n", fi->findex, fi->findex2); - } - write_findex(ua, fi->next, fd); - } -} - - -static void print_findex(UAContext *ua, RBSR_FINDEX *fi) -{ - if (fi) { - if (fi->findex == fi->findex2) { - bsendmsg(ua, "FileIndex=%d\n", fi->findex); - } else { - bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2); - } - print_findex(ua, fi->next); - } -} - -/* Create a new bootstrap record */ -static RBSR *new_bsr() -{ - RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR)); - memset(bsr, 0, sizeof(RBSR)); - return bsr; -} - -/* Free the entire BSR */ -static void free_bsr(RBSR *bsr) -{ - if (bsr) { - free_findex(bsr->fi); - free_bsr(bsr->next); - if (bsr->VolParams) { - free(bsr->VolParams); - } - free(bsr); - } -} - -/* - * Complete the BSR by filling in the VolumeName and - * VolSessionId and VolSessionTime using the JobId - */ -static int complete_bsr(UAContext *ua, RBSR *bsr) -{ - JOB_DBR jr; - - if (bsr) { - 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; - } - bsr->VolSessionId = jr.VolSessionId; - bsr->VolSessionTime = jr.VolSessionTime; - 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; - } - return complete_bsr(ua, bsr->next); - } - return 1; -} - -/* - * Write the bootstrap record to file - */ -static int write_bsr_file(UAContext *ua, RBSR *bsr) -{ - FILE *fd; - POOLMEM *fname = get_pool_memory(PM_MESSAGE); - int stat; - - Mmsg(&fname, "%s/restore.bsr", working_directory); - fd = fopen(fname, "w+"); - if (!fd) { - bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"), - fname, strerror(errno)); - free_pool_memory(fname); - return 0; - } - /* Sort the bsr chain */ - bsr = sort_bsr(bsr); - /* Write them to file */ - write_bsr(ua, bsr, fd); - stat = !ferror(fd); - fclose(fd); - bsendmsg(ua, _("Bootstrap records written to %s\n"), fname); - - /* Tell the user what he will need to mount */ - bsendmsg(ua, _("\nThe restore job will require the following Volumes:\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++) { - add_prompt(ua, nbsr->VolParams[i].VolumeName); - } - } - for (int i=0; i < ua->num_prompts; i++) { - bsendmsg(ua, " %s\n", ua->prompt[i]); - free(ua->prompt[i]); - } - ua->num_prompts = 0; - bsendmsg(ua, "\n"); - free_pool_memory(fname); - return stat; -} - -/* - * First sort the bsr chain, then sort the VolParams - */ -static RBSR *sort_bsr(RBSR *bsr) -{ - if (!bsr) { - return bsr; - } - /* ****FIXME**** sort the bsr chain */ - for (RBSR *nbsr=bsr; nbsr; nbsr=nbsr->next) { - } - return bsr; -} - -static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd) -{ - if (bsr) { - for (int i=0; i < bsr->VolCount; i++) { - 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); - write_findex(ua, bsr->fi, fd); - } - write_bsr(ua, bsr->next, fd); - } -} - -static void print_bsr(UAContext *ua, RBSR *bsr) -{ - if (bsr) { - for (int i=0; i < bsr->VolCount; i++) { - bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); - 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); - } - print_bsr(ua, bsr->next); - } -} - - -/* - * Add a FileIndex to the list of BootStrap records. - * Here we are only dealing with JobId's and the FileIndexes - * associated with those JobIds. - */ -static void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) -{ - RBSR *nbsr; - RBSR_FINDEX *fi, *lfi; - - if (findex == 0) { - return; /* probably a dummy directory */ - } - - if (!bsr->fi) { /* 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; - } - /* 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; - } - } - - 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; - } - } - - /* - * At this point, bsr points to bsr containing 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; - } - /* 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 (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; - } - lfi = fi; - } - /* Add to last place found */ - fi = new_findex(); - fi->findex = findex; - fi->findex2 = findex; - fi->next = lfi->next; - lfi->next = fi; - return; -} - -/* - * This callback routine is responsible for inserting the - * items it gets into the directory tree. For each JobId selected - * this routine is called once for each file. We do not allow - * duplicate filenames, but instead keep the info from the most - * recent file entered (i.e. the JobIds are assumed to be sorted) - */ -static int insert_tree_handler(void *ctx, int num_fields, char **row) -{ - TREE_CTX *tree = (TREE_CTX *)ctx; - char fname[2000]; - TREE_NODE *node, *new_node; - int type; - - strip_trailing_junk(row[1]); - if (*row[1] == 0) { - if (*row[0] != '/') { /* Must be Win32 directory */ - type = TN_DIR_NLS; - } else { - type = TN_DIR; - } - } else { - type = TN_FILE; - } - sprintf(fname, "%s%s", row[0], row[1]); - if (tree->avail_node) { - node = tree->avail_node; - } else { - node = new_tree_node(tree->root, type); - tree->avail_node = node; - } - Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname); - new_node = insert_tree_node(fname, node, tree->root, NULL); - /* Note, if node already exists, save new one for next time */ - if (new_node != node) { - tree->avail_node = node; - } else { - tree->avail_node = NULL; - } - new_node->FileIndex = atoi(row[2]); - new_node->JobId = atoi(row[3]); - new_node->type = type; - new_node->extract = 1; /* extract all by default */ - tree->cnt++; - return 0; -} - - -/* - * Set extract to value passed. We recursively walk - * down the tree setting all children if the - * node is a directory. - */ -static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value) -{ - TREE_NODE *n; - FILE_DBR fdbr; - struct stat statp; - - node->extract = value; - /* For a non-file (i.e. directory), we see all the children */ - if (node->type != TN_FILE) { - for (n=node->child; n; n=n->sibling) { - set_extract(ua, n, tree, value); - } - } else if (value) { - char cwd[2000]; - /* Ordinary file, we get the full path, look up the - * attributes, decode them, and if we are hard linked to - * a file that was saved, we must load that file too. - */ - tree_getpath(node, cwd, sizeof(cwd)); - fdbr.FileId = 0; - fdbr.JobId = node->JobId; - if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { - uint32_t LinkFI; - decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ - /* - * If we point to a hard linked file, traverse the tree to - * find that file, and mark it for restoration as well. It - * must have the Link we just obtained and the same JobId. - */ - if (LinkFI) { - for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) { - if (n->FileIndex == LinkFI && n->JobId == node->JobId) { - n->extract = 1; - break; - } - } - } - } - } -} - -static int markcmd(UAContext *ua, TREE_CTX *tree) -{ - TREE_NODE *node; - - if (ua->argc < 2) - return 1; - if (!tree->node->child) { - return 1; - } - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[1], node->fname, 0) == 0) { - set_extract(ua, node, tree, 1); - } - } - return 1; -} - -static int countcmd(UAContext *ua, TREE_CTX *tree) -{ - int total, extract; - - total = extract = 0; - for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { - if (node->type != TN_NEWDIR) { - total++; - if (node->extract) { - extract++; - } - } - } - bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract); - return 1; -} - -static int findcmd(UAContext *ua, TREE_CTX *tree) -{ - char cwd[2000]; - - if (ua->argc == 1) { - bsendmsg(ua, _("No file specification given.\n")); - return 0; - } - - for (int i=1; i < ua->argc; i++) { - for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - tree_getpath(node, cwd, sizeof(cwd)); - bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd); - } - } - } - return 1; -} - - - -static int lscmd(UAContext *ua, TREE_CTX *tree) -{ - TREE_NODE *node; - - if (!tree->node->child) { - return 1; - } - for (node = tree->node->child; node; node=node->sibling) { - if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { - bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, - (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); - } - } - return 1; -} - -extern char *getuser(uid_t uid); -extern char *getgroup(gid_t gid); - -/* - * This is actually the long form used for "dir" - */ -static void ls_output(char *buf, char *fname, int extract, struct stat *statp) -{ - char *p, *f; - char ec1[30]; - int n; - - p = encode_mode(statp->st_mode, buf); - n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink); - p += n; - n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid)); - p += n; - n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1)); - p += n; - p = encode_time(statp->st_ctime, p); - *p++ = ' '; - if (extract) { - *p++ = '*'; - } else { - *p++ = ' '; - } - for (f=fname; *f; ) - *p++ = *f++; - *p = 0; -} - - -/* - * Like ls command, but give more detail on each file - */ -static int dircmd(UAContext *ua, TREE_CTX *tree) -{ - TREE_NODE *node; - FILE_DBR fdbr; - struct stat statp; - char buf[1000]; - char cwd[1100]; - - if (!tree->node->child) { - return 1; - } - for (node = tree->node->child; node; node=node->sibling) { - if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { - tree_getpath(node, cwd, sizeof(cwd)); - fdbr.FileId = 0; - fdbr.JobId = node->JobId; - if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { - uint32_t LinkFI; - decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ - ls_output(buf, cwd, node->extract, &statp); - bsendmsg(ua, "%s\n", buf); - } else { - /* Something went wrong getting attributes -- print name */ - bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, - (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); - } - } - } - return 1; -} - - -static int helpcmd(UAContext *ua, TREE_CTX *tree) -{ - unsigned int i; - -/* usage(); */ - bsendmsg(ua, _(" Command Description\n ======= ===========\n")); - for (i=0; iargc != 2) { - return 1; - } - node = tree_cwd(ua->argk[1], tree->root, tree->node); - if (!node) { - /* Try once more if Win32 drive -- make absolute */ - if (ua->argk[1][1] == ':') { /* win32 drive */ - strcpy(cwd, "/"); - strcat(cwd, ua->argk[1]); - node = tree_cwd(cwd, tree->root, tree->node); - } - if (!node) { - bsendmsg(ua, _("Invalid path given.\n")); - } else { - tree->node = node; - } - } else { - tree->node = node; - } - tree_getpath(tree->node, cwd, sizeof(cwd)); - bsendmsg(ua, _("cwd is: %s\n"), cwd); - return 1; -} - -static int pwdcmd(UAContext *ua, TREE_CTX *tree) -{ - char cwd[2000]; - tree_getpath(tree->node, cwd, sizeof(cwd)); - bsendmsg(ua, _("cwd is: %s\n"), cwd); - return 1; -} - - -static int unmarkcmd(UAContext *ua, TREE_CTX *tree) -{ - TREE_NODE *node; - - if (ua->argc < 2) - return 1; - if (!tree->node->child) { - return 1; - } - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[1], node->fname, 0) == 0) { - set_extract(ua, node, tree, 0); - } - } - return 1; -} - -static int quitcmd(UAContext *ua, TREE_CTX *tree) -{ - return 0; -} - - /* * Called here with each name to be added to the list. The name is * added to the list if it is not already in the list. + * + * Used to make unique list of FileSets and MediaTypes */ static int unique_name_list_handler(void *ctx, int num_fields, char **row) { diff --git a/bacula/src/dird/ua_tree.c b/bacula/src/dird/ua_tree.c new file mode 100644 index 0000000000..a3ae58683c --- /dev/null +++ b/bacula/src/dird/ua_tree.c @@ -0,0 +1,428 @@ +/* + * + * Bacula Director -- User Agent Database File tree for Restore + * command. + * + * Kern Sibbald, July MMII + * + * Version $Id$ + */ + +/* + Copyright (C) 2002-2003 Kern Sibbald and John Walker + + 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" +#include +#include "findlib/find.h" + + +/* Forward referenced commands */ + +static int markcmd(UAContext *ua, TREE_CTX *tree); +static int countcmd(UAContext *ua, TREE_CTX *tree); +static int findcmd(UAContext *ua, TREE_CTX *tree); +static int lscmd(UAContext *ua, TREE_CTX *tree); +static int dircmd(UAContext *ua, TREE_CTX *tree); +static int helpcmd(UAContext *ua, TREE_CTX *tree); +static int cdcmd(UAContext *ua, TREE_CTX *tree); +static int pwdcmd(UAContext *ua, TREE_CTX *tree); +static int unmarkcmd(UAContext *ua, TREE_CTX *tree); +static int quitcmd(UAContext *ua, TREE_CTX *tree); + + +struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; }; +static struct cmdstruct commands[] = { + { N_("mark"), markcmd, _("mark file for restoration")}, + { N_("unmark"), unmarkcmd, _("unmark file for restoration")}, + { N_("cd"), cdcmd, _("change current directory")}, + { N_("pwd"), pwdcmd, _("print current working directory")}, + { N_("ls"), lscmd, _("list current directory")}, + { N_("dir"), dircmd, _("list current directory")}, + { N_("count"), countcmd, _("count marked files")}, + { N_("find"), findcmd, _("find files")}, + { N_("done"), quitcmd, _("leave file selection mode")}, + { N_("exit"), quitcmd, _("exit = done")}, + { N_("help"), helpcmd, _("print help")}, + { N_("?"), helpcmd, _("print help")}, + }; +#define comsize (sizeof(commands)/sizeof(struct cmdstruct)) + + +/* + * Enter a prompt mode where the user can select/deselect + * files to be restored. This is sort of like a mini-shell + * that allows "cd", "pwd", "add", "rm", ... + */ +void user_select_files_from_tree(TREE_CTX *tree) +{ + char cwd[2000]; + + bsendmsg(tree->ua, _( + "\nYou are now entering file selection mode where you add and\n" + "remove files to be restored. All files are initially added.\n" + "Enter \"done\" to leave this mode.\n\n")); + /* + * Enter interactive command handler allowing selection + * of individual files. + */ + tree->node = (TREE_NODE *)tree->root; + tree_getpath(tree->node, cwd, sizeof(cwd)); + bsendmsg(tree->ua, _("cwd is: %s\n"), cwd); + for ( ;; ) { + int found, len, stat, i; + if (!get_cmd(tree->ua, "$ ")) { + break; + } + parse_ua_args(tree->ua); + if (tree->ua->argc == 0) { + return; + } + + len = strlen(tree->ua->argk[0]); + found = 0; + stat = 0; + for (i=0; i<(int)comsize; i++) /* search for command */ + if (strncasecmp(tree->ua->argk[0], _(commands[i].key), len) == 0) { + stat = (*commands[i].func)(tree->ua, tree); /* go execute command */ + found = 1; + break; + } + if (!found) { + bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n")); + continue; + } + if (!stat) { + break; + } + } +} + + +/* + * This callback routine is responsible for inserting the + * items it gets into the directory tree. For each JobId selected + * this routine is called once for each file. We do not allow + * duplicate filenames, but instead keep the info from the most + * recent file entered (i.e. the JobIds are assumed to be sorted) + */ +int insert_tree_handler(void *ctx, int num_fields, char **row) +{ + TREE_CTX *tree = (TREE_CTX *)ctx; + char fname[2000]; + TREE_NODE *node, *new_node; + int type; + + strip_trailing_junk(row[1]); + if (*row[1] == 0) { + if (*row[0] != '/') { /* Must be Win32 directory */ + type = TN_DIR_NLS; + } else { + type = TN_DIR; + } + } else { + type = TN_FILE; + } + sprintf(fname, "%s%s", row[0], row[1]); + if (tree->avail_node) { + node = tree->avail_node; + } else { + node = new_tree_node(tree->root, type); + tree->avail_node = node; + } + Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname); + new_node = insert_tree_node(fname, node, tree->root, NULL); + /* Note, if node already exists, save new one for next time */ + if (new_node != node) { + tree->avail_node = node; + } else { + tree->avail_node = NULL; + } + new_node->FileIndex = atoi(row[2]); + new_node->JobId = atoi(row[3]); + new_node->type = type; + new_node->extract = 1; /* extract all by default */ + tree->cnt++; + return 0; +} + + +/* + * Set extract to value passed. We recursively walk + * down the tree setting all children if the + * node is a directory. + */ +static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value) +{ + TREE_NODE *n; + FILE_DBR fdbr; + struct stat statp; + + node->extract = value; + /* For a non-file (i.e. directory), we see all the children */ + if (node->type != TN_FILE) { + for (n=node->child; n; n=n->sibling) { + set_extract(ua, n, tree, value); + } + } else if (value) { + char cwd[2000]; + /* Ordinary file, we get the full path, look up the + * attributes, decode them, and if we are hard linked to + * a file that was saved, we must load that file too. + */ + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + fdbr.JobId = node->JobId; + if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { + uint32_t LinkFI; + decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + /* + * If we point to a hard linked file, traverse the tree to + * find that file, and mark it for restoration as well. It + * must have the Link we just obtained and the same JobId. + */ + if (LinkFI) { + for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) { + if (n->FileIndex == LinkFI && n->JobId == node->JobId) { + n->extract = 1; + break; + } + } + } + } + } +} + +static int markcmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + + if (ua->argc < 2) + return 1; + if (!tree->node->child) { + return 1; + } + for (node = tree->node->child; node; node=node->sibling) { + if (fnmatch(ua->argk[1], node->fname, 0) == 0) { + set_extract(ua, node, tree, 1); + } + } + return 1; +} + +static int countcmd(UAContext *ua, TREE_CTX *tree) +{ + int total, extract; + + total = extract = 0; + for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { + if (node->type != TN_NEWDIR) { + total++; + if (node->extract) { + extract++; + } + } + } + bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract); + return 1; +} + +static int findcmd(UAContext *ua, TREE_CTX *tree) +{ + char cwd[2000]; + + if (ua->argc == 1) { + bsendmsg(ua, _("No file specification given.\n")); + return 0; + } + + for (int i=1; i < ua->argc; i++) { + for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + tree_getpath(node, cwd, sizeof(cwd)); + bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd); + } + } + } + return 1; +} + + + +static int lscmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + + if (!tree->node->child) { + return 1; + } + for (node = tree->node->child; node; node=node->sibling) { + if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { + bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); + } + } + return 1; +} + +extern char *getuser(uid_t uid); +extern char *getgroup(gid_t gid); + +/* + * This is actually the long form used for "dir" + */ +static void ls_output(char *buf, char *fname, int extract, struct stat *statp) +{ + char *p, *f; + char ec1[30]; + int n; + + p = encode_mode(statp->st_mode, buf); + n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink); + p += n; + n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid)); + p += n; + n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1)); + p += n; + p = encode_time(statp->st_ctime, p); + *p++ = ' '; + if (extract) { + *p++ = '*'; + } else { + *p++ = ' '; + } + for (f=fname; *f; ) + *p++ = *f++; + *p = 0; +} + + +/* + * Like ls command, but give more detail on each file + */ +static int dircmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + FILE_DBR fdbr; + struct stat statp; + char buf[1000]; + char cwd[1100]; + + if (!tree->node->child) { + return 1; + } + for (node = tree->node->child; node; node=node->sibling) { + if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + fdbr.JobId = node->JobId; + if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { + uint32_t LinkFI; + decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + ls_output(buf, cwd, node->extract, &statp); + bsendmsg(ua, "%s\n", buf); + } else { + /* Something went wrong getting attributes -- print name */ + bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); + } + } + } + return 1; +} + + +static int helpcmd(UAContext *ua, TREE_CTX *tree) +{ + unsigned int i; + +/* usage(); */ + bsendmsg(ua, _(" Command Description\n ======= ===========\n")); + for (i=0; iargc != 2) { + return 1; + } + node = tree_cwd(ua->argk[1], tree->root, tree->node); + if (!node) { + /* Try once more if Win32 drive -- make absolute */ + if (ua->argk[1][1] == ':') { /* win32 drive */ + strcpy(cwd, "/"); + strcat(cwd, ua->argk[1]); + node = tree_cwd(cwd, tree->root, tree->node); + } + if (!node) { + bsendmsg(ua, _("Invalid path given.\n")); + } else { + tree->node = node; + } + } else { + tree->node = node; + } + tree_getpath(tree->node, cwd, sizeof(cwd)); + bsendmsg(ua, _("cwd is: %s\n"), cwd); + return 1; +} + +static int pwdcmd(UAContext *ua, TREE_CTX *tree) +{ + char cwd[2000]; + tree_getpath(tree->node, cwd, sizeof(cwd)); + bsendmsg(ua, _("cwd is: %s\n"), cwd); + return 1; +} + + +static int unmarkcmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + + if (ua->argc < 2) + return 1; + if (!tree->node->child) { + return 1; + } + for (node = tree->node->child; node; node=node->sibling) { + if (fnmatch(ua->argk[1], node->fname, 0) == 0) { + set_extract(ua, node, tree, 0); + } + } + return 1; +} + +static int quitcmd(UAContext *ua, TREE_CTX *tree) +{ + return 0; +} diff --git a/bacula/src/version.h b/bacula/src/version.h index d0a233c613..113c8b32f9 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #define VERSION "1.31" #define VSTRING "1" -#define BDATE "01 Jun 2003" -#define LSMDATE "01Jun03" +#define BDATE "02 Jun 2003" +#define LSMDATE "02Jun03" /* Debug flags */ #define DEBUG 1 -- 2.39.5