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.
- 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.
- 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.
-
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);
#
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 \
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 \
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@
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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 */
+};
#include "jcr.h"
+#include "bsr.h"
#include "ua.h"
#include "protos.h"
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);
/* 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);
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);
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
/*
*
* 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
*
#include "bacula.h"
#include "dird.h"
-#include <fnmatch.h>
-#include "findlib/find.h"
-
/* Imported functions */
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;
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 */
/* 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);
/*
}
}
- 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 */
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
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; i<comsize; i++) {
- bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
- }
- bsendmsg(ua, "\n");
- return 1;
-}
-
-/*
- * Change directories. Note, if the user specifies x: and it fails,
- * we assume it is a Win32 absolute cd rather than relative and
- * try a second time with /x: ... Win32 kludge.
- */
-static int cdcmd(UAContext *ua, TREE_CTX *tree)
-{
- TREE_NODE *node;
- char cwd[2000];
-
- if (ua->argc != 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)
{
--- /dev/null
+/*
+ *
+ * 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 <fnmatch.h>
+#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; i<comsize; i++) {
+ bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
+ }
+ bsendmsg(ua, "\n");
+ return 1;
+}
+
+/*
+ * Change directories. Note, if the user specifies x: and it fails,
+ * we assume it is a Win32 absolute cd rather than relative and
+ * try a second time with /x: ... Win32 kludge.
+ */
+static int cdcmd(UAContext *ua, TREE_CTX *tree)
+{
+ TREE_NODE *node;
+ char cwd[2000];
+
+ if (ua->argc != 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;
+}
/* */
#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