Kern's ToDo List
- 29 October 2002
+ 30 October 2002
To do:
- Document that two Verifys at same time on same client do not work.
- Document how to recycle a tape in 7 days even if the backup takes a long time.
-
-- Figure out why my Catalog size keeps growing (Filename and Path
- tables keep growing) -- fix it.
-
-- Add Doc on saving MySQL databases, where to find code for shutting
+- Document to have patience when SD first starts.
+- Document running a test version.
+- Document buffer size considerations with Sparse files --
+- Document saving MySQL databases, where to find code for shutting
down and saving other databases.
For 1.27 release:
- Continue improving the restore process (handling
of tapes, efficiency improvements e.g. use FSF to
position the tape, ...)
-- Allow restore with options (e.g. overwrite, overwrite
- if newer, no overwrite, permit changing ownership, ...).
-- Test recovery of a damaged tape.
- Work more on how to to a Bacula restore beginning with
just a Bacula tape and a boot floppy (bare metal recovery).
-- Possibly add a File System Module permitting an external
- program do the file reading and writing during save and
- restore (i.e. with this you could backup/restore
- ANYTHING to and from a Bacula Volume, including horrible
- stuff like Windows ACLs).
+- Finish implementation of restore "replace" options, and document.
- Write bcopy program.
-- Preprocessing command per file.
-- Postprocessing command per file (when restoring).
-- File system type from File daemon
+- Recovery of a bad tape (bcopy)
+
+- Program files (i.e. execute a program to read/write files).
+ Pass read date of last backup, size of file last time.
+
+- Put system type returned by FD into catalog.
- Decide what to do with JobTDate in catalog (make real btime_t?)
-- File daemon should pass Director the operating system info
- to be stored in the Client Record (or verified that it has
- not changed).
- Add VOLUME_CAT_INFO to the EOS tape record (as
well as to the EOD record).
-- Send Volumes needed during restore to Console (just after
- create_volume_list) -- also in restore command?
-- Document buffer size considerations with Sparse files --
-- Move block size code from block.c to init_dev().
- Add code to fast seek to proper place on tape/file
when doing Restore. If it doesn't work, try linear
search as before.
long and a job is waiting on the drive.
- What to do with btime and JobTDate?
- Add FileSet MD5 to bscan.
-- Finish implementation of restore "replace" options, and document.
- Strip trailing slashes from Include directory names in the FD.
- Use read_record.c in SD code.
-- Allow changing ownership/group of files on restore.
-- Restore options (overwrite, ...)
-- Program files (i.e. execute a program to read/write files).
- Pass read date of last backup, size of file last time.
- Try bare metal Windows restore
-- Recovery of a bad tape (bcopy)
-- EOM records?????????????????
+- Add EOM records ???????
- Why don't we get an error message from Win32 FD when bootstrap
file cannot be created for restore command?
-- Put MaximumVolumeSize in Director.
-- Document to have patience when SD first starts.
-- Document running a test version.
+- Put MaximumVolumeSize in Director (MaximumVolumeJobs, MaximumVolumeFiles,
+ MaximumFileSize).
- When Marking a file in Restore that is a hard link, also
mark the link so that the data will be reloaded.
- Restore program that errors in SD due to no tape reports
in block, done when count is reached, and possibly other
optimizations. I.e. add a state word.
+Test:
+- that restore prints volumes.
+- that restore options work in FD.
+- that mod of restore options works.
+- that Bacula won't write on tape where tape/catalog files differ.
+- that console command line options work
+
After 1.27
+- Make sure catalog doesn't keep growing.
+- Permit changing ownership during restore.
- After unmount, if restore job started, ask to mount.
- Fix db_get_fileset in cats/sql_get.c for multiple records.
- Fix start/end blocks for File devices
- Document all daemon tools MUST have a config file.
- Why does btape error when pointed to a file?
- Disallow compile if long long not 64 bits.
+- Send Volumes needed during restore to Console (just after
+ create_volume_list) -- also in restore command?
+- File system type from File daemon
+- Move block size code from block.c to init_dev().
+
static void store_restore(LEX *lc, struct res_items *item, int index, int pass);
static void store_jobtype(LEX *lc, struct res_items *item, int index, int pass);
static void store_level(LEX *lc, struct res_items *item, int index, int pass);
+static void store_replace(LEX *lc, struct res_items *item, int index, int pass);
/* We build the current resource here as we are
{"client", store_res, ITEM(res_job.client), R_CLIENT, 0, 0},
{"fileset", store_res, ITEM(res_job.fileset), R_FILESET, 0, 0},
{"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
+ {"replace", store_replace, ITEM(res_job.replace), 'a', ITEM_DEFAULT, 0},
{"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
{"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
{"maxstartdelay", store_time,ITEM(res_job.MaxStartDelay), 0, 0, 0},
};
/* Options permitted in Restore replace= */
-static struct s_kw ReplaceOptions[] = {
+struct s_kw ReplaceOptions[] = {
{"always", 'a'}, /* always */
{"ifnewer", 'w'},
{"never", 'n'},
set_bit(index, res_all.hdr.item_present);
}
-
+static void store_replace(LEX *lc, struct res_items *item, int index, int pass)
+{
+ int token, i;
+ token = lex_get_token(lc, T_NAME);
+ /* Scan Replacement options */
+ for (i=0; ReplaceOptions[i].name; i++) {
+ if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
+ ((JOB *)(item->value))->replace = ReplaceOptions[i].token;
+ i = 0;
+ break;
+ }
+ }
+ if (i != 0) {
+ scan_err1(lc, "Expected a Restore replacement option, got: %s", lc->str);
+ }
+ scan_to_eol(lc);
+ set_bit(index, res_all.hdr.item_present);
+}
/*
* Store backup/verify info for Job record
/* Fix to scan Replacement options */
for (i=0; ReplaceOptions[i].name; i++) {
if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
- ((JOB *)(item->value))->RestoreOptions = ReplaceOptions[i].token;
+ ((JOB *)(item->value))->replace = ReplaceOptions[i].token;
i = 0;
break;
}
/*
* Resource codes -- they must be sequential for indexing
*/
-#define R_FIRST 1001
-
-#define R_DIRECTOR 1001
-#define R_CLIENT 1002
-#define R_JOB 1003
-#define R_STORAGE 1004
-#define R_CATALOG 1005
-#define R_SCHEDULE 1006
-#define R_FILESET 1007
-#define R_GROUP 1008
-#define R_POOL 1009
-#define R_MSGS 1010
-#define R_COUNTER 1011
-
-#define R_LAST R_COUNTER
+#define R_FIRST 1001
+
+#define R_DIRECTOR 1001
+#define R_CLIENT 1002
+#define R_JOB 1003
+#define R_STORAGE 1004
+#define R_CATALOG 1005
+#define R_SCHEDULE 1006
+#define R_FILESET 1007
+#define R_GROUP 1008
+#define R_POOL 1009
+#define R_MSGS 1010
+#define R_COUNTER 1011
+
+#define R_LAST R_COUNTER
/*
* Some resource attributes
*/
-#define R_NAME 1020
-#define R_ADDRESS 1021
-#define R_PASSWORD 1022
-#define R_TYPE 1023
-#define R_BACKUP 1024
+#define R_NAME 1020
+#define R_ADDRESS 1021
+#define R_PASSWORD 1022
+#define R_TYPE 1023
+#define R_BACKUP 1024
/* Used for certain KeyWord tables */
-struct s_kw {
+struct s_kw {
char *name;
- int token;
+ int token;
};
/* Job Level keyword structure */
struct s_jl {
- char *level_name; /* level keyword */
- int level; /* level */
- int job_type; /* JobType permitting this level */
+ char *level_name; /* level keyword */
+ int level; /* level */
+ int job_type; /* JobType permitting this level */
};
/* Job Type keyword structure */
/* Definition of the contents of each Resource */
/*
- * Director Resource
+ * Director Resource
*
*/
struct s_res_dir {
- RES hdr;
- int DIRport; /* where we listen -- UA port server port */
- char *DIRaddr; /* bind address */
- char *password; /* Password for UA access */
- char *query_file; /* SQL query file */
- char *working_directory; /* WorkingDirectory */
- char *pid_directory; /* PidDirectory */
- char *subsys_directory; /* SubsysDirectory */
+ RES hdr;
+ int DIRport; /* where we listen -- UA port server port */
+ char *DIRaddr; /* bind address */
+ char *password; /* Password for UA access */
+ char *query_file; /* SQL query file */
+ char *working_directory; /* WorkingDirectory */
+ char *pid_directory; /* PidDirectory */
+ char *subsys_directory; /* SubsysDirectory */
struct s_res_msgs *messages; /* Daemon message handler */
- int MaxConcurrentJobs;
- btime_t FDConnectTimeout; /* timeout for connect in seconds */
- btime_t SDConnectTimeout; /* timeout in seconds */
+ int MaxConcurrentJobs;
+ btime_t FDConnectTimeout; /* timeout for connect in seconds */
+ btime_t SDConnectTimeout; /* timeout in seconds */
};
typedef struct s_res_dir DIRRES;
*
*/
struct s_res_client {
- RES hdr;
+ RES hdr;
- int FDport; /* Where File daemon listens */
- int AutoPrune; /* Do automatic pruning? */
- btime_t FileRetention; /* file retention period in seconds */
- btime_t JobRetention; /* job retention period in seconds */
+ int FDport; /* Where File daemon listens */
+ int AutoPrune; /* Do automatic pruning? */
+ btime_t FileRetention; /* file retention period in seconds */
+ btime_t JobRetention; /* job retention period in seconds */
char *address;
char *password;
struct s_res_cat *catalog; /* Catalog resource */
*
*/
struct s_res_store {
- RES hdr;
+ RES hdr;
- int SDport; /* port where Directors connect */
- int SDDport; /* data port for File daemon */
+ int SDport; /* port where Directors connect */
+ int SDDport; /* data port for File daemon */
char *address;
char *password;
char *media_type;
char *dev_name;
- int autochanger; /* set if autochanger */
+ int autochanger; /* set if autochanger */
};
typedef struct s_res_store STORE;
*
*/
struct s_res_cat {
- RES hdr;
+ RES hdr;
- int DBport; /* Port -- not yet implemented */
+ int DBport; /* Port -- not yet implemented */
char *address;
char *db_password;
char *db_user;
*
*/
struct s_res_job {
- RES hdr;
-
- int JobType; /* job type (backup, verify, restore */
- int level; /* default backup/verify level */
- int RestoreJobId; /* What -- JobId to restore */
- char *RestoreWhere; /* Where on disk to restore -- directory */
- char *RestoreBootstrap; /* Bootstrap file */
- char *RunBeforeJob; /* Run program before Job */
- char *RunAfterJob; /* Run program after Job */
- char *WriteBootstrap; /* Where to write bootstrap Job updates */
- int RestoreOptions; /* How (overwrite, ..) */
- btime_t MaxRunTime; /* max run time in seconds */
- btime_t MaxStartDelay; /* max start delay in seconds */
- int PruneJobs; /* Force pruning of Jobs */
- int PruneFiles; /* Force pruning of Files */
- int PruneVolumes; /* Force pruning of Volumes */
- int SpoolAttributes; /* Set to spool attributes in SD */
+ RES hdr;
+
+ int JobType; /* job type (backup, verify, restore */
+ int level; /* default backup/verify level */
+ int RestoreJobId; /* What -- JobId to restore */
+ char *RestoreWhere; /* Where on disk to restore -- directory */
+ char *RestoreBootstrap; /* Bootstrap file */
+ char *RunBeforeJob; /* Run program before Job */
+ char *RunAfterJob; /* Run program after Job */
+ char *WriteBootstrap; /* Where to write bootstrap Job updates */
+ int replace; /* How (overwrite, ..) */
+ btime_t MaxRunTime; /* max run time in seconds */
+ btime_t MaxStartDelay; /* max start delay in seconds */
+ int PruneJobs; /* Force pruning of Jobs */
+ int PruneFiles; /* Force pruning of Files */
+ int PruneVolumes; /* Force pruning of Volumes */
+ int SpoolAttributes; /* Set to spool attributes in SD */
struct s_res_msgs *messages; /* How and where to send messages */
struct s_res_sch *schedule; /* When -- Automatic schedule */
struct s_res_client *client; /* Who to backup */
struct s_res_fs *fileset; /* What to backup -- Fileset */
struct s_res_store *storage; /* Where is device -- Storage daemon */
- struct s_res_pool *pool; /* Where is media -- Media Pool */
+ struct s_res_pool *pool; /* Where is media -- Media Pool */
};
typedef struct s_res_job JOB;
*
*/
struct s_res_fs {
- RES hdr;
+ RES hdr;
char **include_array;
int num_includes;
char **exclude_array;
int num_excludes;
int exclude_size;
- int have_MD5; /* set if MD5 initialized */
- struct MD5Context md5c; /* MD5 of include/exclude */
- char MD5[50]; /* base 64 representation of MD5 */
+ int have_MD5; /* set if MD5 initialized */
+ struct MD5Context md5c; /* MD5 of include/exclude */
+ char MD5[50]; /* base 64 representation of MD5 */
};
typedef struct s_res_fs FILESET;
*
*/
struct s_res_sch {
- RES hdr;
+ RES hdr;
struct s_run *run;
};
*
*/
struct s_res_group {
- RES hdr;
+ RES hdr;
};
typedef struct s_res_group GROUP;
* Counter Resource
*/
struct s_res_counter {
- RES hdr;
+ RES hdr;
- int32_t MinValue; /* Minimum value */
- int32_t MaxValue; /* Maximum value */
- int Global; /* global/local */
- char *WrapCounter; /* Wrap counter name */
+ int32_t MinValue; /* Minimum value */
+ int32_t MaxValue; /* Maximum value */
+ int Global; /* global/local */
+ char *WrapCounter; /* Wrap counter name */
};
typedef struct s_res_counter COUNTER;
*
*/
struct s_res_pool {
- RES hdr;
+ RES hdr;
struct s_res_counter counter; /* Counter resources */
- char *pool_type; /* Pool type */
- char *label_format; /* Label format string */
- int use_catalog; /* maintain catalog for media */
- int catalog_files; /* maintain file entries in catalog */
- int use_volume_once; /* write on volume only once */
- int accept_any_volume; /* accept any volume */
- int max_volumes; /* max number of volumes */
- btime_t VolRetention; /* volume retention period in seconds */
- int AutoPrune; /* default for pool auto prune */
- int Recycle; /* default for media recycle yes/no */
+ char *pool_type; /* Pool type */
+ char *label_format; /* Label format string */
+ int use_catalog; /* maintain catalog for media */
+ int catalog_files; /* maintain file entries in catalog */
+ int use_volume_once; /* write on volume only once */
+ int accept_any_volume; /* accept any volume */
+ int max_volumes; /* max number of volumes */
+ btime_t VolRetention; /* volume retention period in seconds */
+ int AutoPrune; /* default for pool auto prune */
+ int Recycle; /* default for media recycle yes/no */
};
typedef struct s_res_pool POOL;
* resource structure definitions.
*/
union u_res {
- struct s_res_dir res_dir;
- struct s_res_client res_client;
- struct s_res_store res_store;
- struct s_res_cat res_cat;
- struct s_res_job res_job;
- struct s_res_fs res_fs;
- struct s_res_sch res_sch;
- struct s_res_group res_group;
- struct s_res_pool res_pool;
- struct s_res_msgs res_msgs;
+ struct s_res_dir res_dir;
+ struct s_res_client res_client;
+ struct s_res_store res_store;
+ struct s_res_cat res_cat;
+ struct s_res_job res_job;
+ struct s_res_fs res_fs;
+ struct s_res_sch res_sch;
+ struct s_res_group res_group;
+ struct s_res_pool res_pool;
+ struct s_res_msgs res_msgs;
struct s_res_counter res_counter;
RES hdr;
};
/* Run structure contained in Schedule Resource */
struct s_run {
- struct s_run *next; /* points to next run record */
- int level; /* level override */
+ struct s_run *next; /* points to next run record */
+ int level; /* level override */
int job_type;
- POOL *pool; /* Pool override */
- STORE *storage; /* Storage override */
- MSGS *msgs; /* Messages override */
+ POOL *pool; /* Pool override */
+ STORE *storage; /* Storage override */
+ MSGS *msgs; /* Messages override */
char *since;
int level_no;
- int minute; /* minute to run job */
- time_t last_run; /* last time run */
- time_t next_run; /* next time to run */
+ int minute; /* minute to run job */
+ time_t last_run; /* last time run */
+ time_t next_run; /* next time to run */
char hour[nbytes_for_bits(24)]; /* bit set for each hour */
char mday[nbytes_for_bits(31)]; /* bit set for each day of month */
char month[nbytes_for_bits(12)]; /* bit set for each month */
/* Send restore command */
char replace, *where;
- if (jcr->job->RestoreOptions != 0) {
- replace = jcr->job->RestoreOptions;
+ if (jcr->job->replace != 0) {
+ replace = jcr->job->replace;
} else {
replace = 'a'; /* always replace */
}
FILE *fd;
POOLMEM *fname = get_pool_memory(PM_MESSAGE);
int stat;
+ RBSR *nbsr;
Mmsg(&fname, "%s/restore.bsr", working_directory);
fd = fopen(fname, "w+");
write_bsr(ua, bsr, fd);
stat = !ferror(fd);
fclose(fd);
-// bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
+ bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
+ bsendmsg(ua, _("\nThe restore job will require the following Volumes:\n"));
+ for (nbsr=bsr; nbsr; nbsr=nbsr->next) {
+ if (nbsr->VolumeName) {
+ bsendmsg(ua, " %s\n", nbsr->VolumeName);
+ }
+ }
+ bsendmsg(ua, "\n");
free_pool_memory(fname);
return stat;
}
/* Imported variables */
extern struct s_jl joblevels[];
+extern struct s_kw ReplaceOptions[];
/*
* For Backup and Verify Jobs
{
JCR *jcr;
char *job_name, *level_name, *jid, *store_name;
- char *where, *fileset_name, *client_name, *bootstrap;
- int i, j, found;
+ char *where, *fileset_name, *client_name, *bootstrap, *replace;
+ int i, j, found, opt;
JOB *job = NULL;
STORE *store = NULL;
CLIENT *client = NULL;
N_("storage"),
N_("where"),
N_("bootstrap"),
+ N_("replace"),
NULL};
if (!open_db(ua)) {
client_name = NULL;
fileset_name = NULL;
bootstrap = NULL;
+ replace = NULL;
for (i=1; i<ua->argc; i++) {
found = False;
bootstrap = ua->argv[i];
found = True;
break;
+ case 8: /* replace */
+ if (replace) {
+ bsendmsg(ua, _("Replace specified twice.\n"));
+ return 1;
+ }
+ replace = ua->argv[i];
+ found = True;
+ break;
default:
break;
}
jcr->RestoreBootstrap = bstrdup(bootstrap);
}
-
-
+ if (replace) {
+ jcr->replace = 0;
+ for (i=0; ReplaceOptions[i].name; i++) {
+ if (strcasecmp(replace, ReplaceOptions[i].name) == 0) {
+ jcr->replace = ReplaceOptions[i].token;
+ }
+ }
+ if (!jcr->replace) {
+ bsendmsg(ua, _("Invalid replace option: %s\n"), replace);
+ return 0;
+ }
+ } else if (job->replace) {
+ jcr->replace = job->replace;
+ } else {
+ jcr->replace = 'a';
+ }
+ replace = ReplaceOptions[0].name;
+ for (i=0; ReplaceOptions[i].name; i++) {
+ if (ReplaceOptions[i].token == jcr->replace) {
+ replace = ReplaceOptions[i].name;
+ }
+ }
try_again:
Dmsg1(20, "JobType=%c\n", jcr->JobType);
JobName: %s\n\
Bootstrap: %s\n\
Where: %s\n\
+Replace: %s\n\
FileSet: %s\n\
Client: %s\n\
Storage: %s\n\
job->hdr.name,
NPRT(jcr->RestoreBootstrap),
jcr->RestoreWhere?jcr->RestoreWhere:NPRT(job->RestoreWhere),
+ replace,
jcr->fileset->hdr.name,
jcr->client->hdr.name,
jcr->store->hdr.name,
if (jcr->JobType == JT_RESTORE) {
add_prompt(ua, _("Bootstrap")); /* 5 */
add_prompt(ua, _("Where")); /* 6 */
- add_prompt(ua, _("JobId")); /* 7 */
+ add_prompt(ua, _("Replace")); /* 7 */
+ add_prompt(ua, _("JobId")); /* 8 */
}
switch (do_prompt(ua, _("Select parameter to modify"), NULL)) {
case 0:
jcr->RestoreWhere = bstrdup(ua->cmd);
goto try_again;
case 7:
+ /* Replace */
+ start_prompt(ua, _("Replace:\n"));
+ for (i=0; ReplaceOptions[i].name; i++) {
+ add_prompt(ua, ReplaceOptions[i].name);
+ }
+ opt = do_prompt(ua, _("Select replace option"), NULL);
+ if (opt <= 0) {
+ jcr->replace = ReplaceOptions[opt].token;
+ }
+ goto try_again;
+ case 8:
/* JobId */
jid = NULL; /* force reprompt */
jcr->RestoreJobId = 0;
uint32_t RestoreJobId; /* Id specified by UA */
char *RestoreWhere; /* Where to restore the files */
POOLMEM *client_uname; /* client uname */
+ int replace; /* Replace option */
#endif /* DIRECTOR_DAEMON */
#ifdef FILE_DAEMON
bls
bscan
btape
+bcopy
btraceback
btraceback.gdb
file1Job1.bsr
acquire.o mount.o record.o match_bsr.o parse_bsr.o \
butil.o read_record.o stored_conf.o
-
-# bpool is deprecated
-#POOLSRCS = bpool.c block.c dev.c device.c askdir.c label.c \
-# record.c stored_conf.c
-#POOLOBJS = bpool.o block.o dev.o device.o askdir.o label.o \
-# record.o stored_conf.o
-
-
+# bcopy
+COPYOBJS = bcopy.o block.o device.o dev.o label.o \
+ acquire.o mount.o record.o match_bsr.o parse_bsr.o \
+ butil.o read_record.o stored_conf.o
.c.o:
$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
#-------------------------------------------------------------------------
-all: Makefile bacula-sd bls bextract bscan btape
+all: Makefile bacula-sd bls bextract bscan btape
@echo "===== Make of stored is good ===="
@echo " "
bacula-sd: $(SVROBJS) ../lib/libbac.a
$(CXX) $(LDFLAGS) -L../lib -o $@ $(SVROBJS) $(FDLIBS) -lbac -lm $(LIBS) $(DLIB)
-#bpool: $(POOLOBJS) ../lib/libbac.a ../cats/libsql.a
-# $(CXX) $(LDFLAGS) -L../lib -L../cats -o $@ $(POOLOBJS) -lsql $(LIBS) $(DLIB) -lbac -lm
-
btape: $(TAPEOBJS) ../lib/libbac.a ../cats/libsql.a
$(CXX) $(TTOOL_LDFLSGS) $(LDFLAGS) -L../lib -L../cats -o $@ $(TAPEOBJS) -lsql $(LIBS) $(DLIB) -lbac -lm
bextract: ../findlib/libfind.a $(BEXTOBJS) ../lib/libbac.a
$(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(BEXTOBJS) $(LIBS) $(DLIB) $(FDLIBS) -lfind -lbac -lm
-bscan: ../findlib/libfind.a $(SCNOBJS) ../cats/libsql.a
+bscan: ../findlib/libfind.a $(SCNOBJS) ../cats/libsql.a
$(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../cats -L../findlib -o $@ $(SCNOBJS) -lsql $(LIBS) $(DB_LIBS) $(FDLIBS) -lfind -lbac -lm
-
+bcopy: $(COPYOBJS) ../findlib/libfind.a ../lib/libbac.a
+ $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(COPYOBJS) $(LIBS) $(DB_LIBS) $(FDLIBS) -lfind -lbac -lm
Makefile: $(srcdir)/Makefile.in $(topdir)/config.status
$(INSTALL_PROGRAM) bacula-sd $(DESTDIR)$(sbindir)/bacula-sd
$(INSTALL_PROGRAM) bls $(DESTDIR)$(sbindir)/bls
$(INSTALL_PROGRAM) bextract $(DESTDIR)$(sbindir)/bextract
-# $(INSTALL_PROGRAM) bpool $(DESTDIR)$(sbindir)/bpool
+ $(INSTALL_PROGRAM) bcopy $(DESTDIR)$(sbindir)/bcopy
+ $(INSTALL_PROGRAM) bscan $(DESTDIR)$(sbindir)/bscan
$(INSTALL_PROGRAM) btape $(DESTDIR)$(sbindir)/btape
@srcconf=bacula-sd.conf; \
if test -f ${DESTDIR}${sysconfdir}/$$srcconf; then \
(cd $(DESTDIR)$(sbindir); $(RMF) bacula-sd)
(cd $(DESTDIR)$(sbindir); $(RMF) bls)
(cd $(DESTDIR)$(sbindir); $(RMF) bextract)
-# (cd $(DESTDIR)$(sbindir); $(RMF) bpool)
+ (cd $(DESTDIR)$(sbindir); $(RMF) bcopy)
+ (cd $(DESTDIR)$(sbindir); $(RMF) bscan)
+ (cd $(DESTDIR)$(sbindir); $(RMF) btape)
(cd $(DESTDIR)$(sysconfdir); $(RMF) bacula-sd.conf)
clean:
@$(RMF) bacula-sd stored bls bextract bpool btape shmfree core core.* a.out *.o *.bak *~ *.intpro *.extpro 1 2 3
- @$(RMF) bscan
+ @$(RMF) bscan bcopy
realclean: clean
@$(RMF) tags bacula-sd.conf
--- /dev/null
+/*
+ *
+ * Program to copy a Bacula from one volume to another.
+ *
+ * Kern E. Sibbald, October 2002
+ *
+ *
+ * Version $Id$
+ */
+/*
+ Copyright (C) 2001, 2002 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 "stored.h"
+
+/* Forward referenced functions */
+static void do_scan(void);
+static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
+
+
+/* Global variables */
+static DEVICE *dev = NULL;
+static B_DB *db;
+static JCR *bjcr; /* jcr for bscan */
+static BSR *bsr = NULL;
+static char *wd = "/tmp";
+static int verbose = 0;
+
+#define CONFIG_FILE "bacula-sd.conf"
+char *configfile;
+
+
+static void usage()
+{
+ fprintf(stderr, _(
+"\nVersion: " VERSION " (" DATE ")\n\n"
+"Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
+" -b bootstrap specify a bootstrap file\n"
+" -c <file> specify configuration file\n"
+" -dnn set debug level to nn\n"
+" -v verbose\n"
+" -w dir specify working directory (default /tmp)\n"
+" -? print this message\n\n"));
+ exit(1);
+}
+
+int main (int argc, char *argv[])
+{
+ int ch;
+
+ my_name_is(argc, argv, "bscan");
+ init_msg(NULL, NULL);
+
+ fprintf(stderr, "\n\nPlease don't use this program.\n\
+It is currently under development and does not work.\n\n");
+
+ while ((ch = getopt(argc, argv, "b:c:d:mn:p:rsu:vw:?")) != -1) {
+ switch (ch) {
+ case 'b':
+ bsr = parse_bsr(NULL, optarg);
+ break;
+
+ case 'c': /* specify config file */
+ if (configfile != NULL) {
+ free(configfile);
+ }
+ configfile = bstrdup(optarg);
+ break;
+
+ case 'd': /* debug level */
+ debug_level = atoi(optarg);
+ if (debug_level <= 0)
+ debug_level = 1;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'w':
+ wd = optarg;
+ break;
+
+ case '?':
+ default:
+ usage();
+
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2) {
+ Pmsg0(0, _("Wrong number of arguments: \n"));
+ usage();
+ }
+
+ working_directory = wd;
+
+ if (configfile == NULL) {
+ configfile = bstrdup(CONFIG_FILE);
+ }
+
+ parse_config(configfile);
+
+ bjcr = setup_jcr("bcopy", argv[0], bsr);
+ dev = setup_to_access_device(bjcr, 0); /* read device */
+ if (!dev) {
+ exit(1);
+ }
+
+ do_copy();
+
+ free_jcr(bjcr);
+ return 0;
+}
+
+
+static void do_copy()
+{
+ detach_jcr_from_device(dev, bjcr);
+
+ read_records(bjcr, dev, record_cb, mount_next_read_volume);
+ release_device(bjcr, dev);
+
+ term_dev(dev);
+}
+
+static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
+{
+ JCR *mjcr;
+ char ec1[30];
+
+ if (list_records) {
+ Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
+ rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
+ rec->Stream, rec->data_len);
+ }
+ /*
+ * Check for Start or End of Session Record
+ *
+ */
+ if (rec->FileIndex < 0) {
+
+ if (verbose > 1) {
+ dump_label_record(dev, rec, 1);
+ }
+ switch (rec->FileIndex) {
+ case PRE_LABEL:
+ Pmsg0(000, "Volume is prelabeled. This volume cannot be copied.\n");
+ return;
+ break;
+ case VOL_LABEL:
+ Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
+ break;
+ case SOS_LABEL:
+ break;
+ case EOS_LABEL:
+ break;
+ case EOM_LABEL:
+ break;
+ case EOT_LABEL: /* end of all tapes */
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ /* Write record */
+
+ return;
+}
+
+/*
+ * Free the Job Control Record if no one is still using it.
+ * Called from main free_jcr() routine in src/lib/jcr.c so
+ * that we can do our Director specific cleanup of the jcr.
+ */
+static void dird_free_jcr(JCR *jcr)
+{
+ Dmsg0(200, "Start dird free_jcr\n");
+
+ if (jcr->file_bsock) {
+ Dmsg0(200, "Close File bsock\n");
+ bnet_close(jcr->file_bsock);
+ }
+ if (jcr->store_bsock) {
+ Dmsg0(200, "Close Store bsock\n");
+ bnet_close(jcr->store_bsock);
+ }
+ if (jcr->RestoreBootstrap) {
+ free(jcr->RestoreBootstrap);
+ }
+ Dmsg0(200, "End dird free_jcr\n");
+}
+
+
+
+/*
+ * Create a JCR as if we are really starting the job
+ */
+static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
+{
+ JCR *jobjcr;
+ /*
+ * Transfer as much as possible to the Job JCR. Most important is
+ * the JobId and the ClientId.
+ */
+ jobjcr = new_jcr(sizeof(JCR), dird_free_jcr);
+ jobjcr->JobType = jr->Type;
+ jobjcr->JobLevel = jr->Level;
+ jobjcr->JobStatus = jr->JobStatus;
+ strcpy(jobjcr->Job, jr->Job);
+ jobjcr->JobId = JobId; /* this is JobId on tape */
+ jobjcr->sched_time = jr->SchedTime;
+ jobjcr->start_time = jr->StartTime;
+ jobjcr->VolSessionId = rec->VolSessionId;
+ jobjcr->VolSessionTime = rec->VolSessionTime;
+ jobjcr->ClientId = jr->ClientId;
+ attach_jcr_to_device(dev, jobjcr);
+ return jobjcr;
+}
+
+/* Dummies to replace askdir.c */
+int dir_get_volume_info(JCR *jcr, int writing) { return 1;}
+int dir_find_next_appendable_volume(JCR *jcr) { return 1;}
+int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int dir_send_job_status(JCR *jcr) {return 1;}
+
+
+int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
+{
+ /*
+ * We are at the end of reading a tape. Now, we simulate handling
+ * the end of writing a tape by wiffling through the attached
+ * jcrs creating jobmedia records.
+ */
+ Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
+ for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
+ if (verbose) {
+ Pmsg1(000, "create JobMedia for Job %s\n", mjcr->Job);
+ }
+ if (dev->state & ST_TAPE) {
+ mjcr->EndBlock = dev->block_num;
+ mjcr->EndFile = dev->file;
+ } else {
+ mjcr->EndBlock = (uint32_t)dev->file_addr;
+ mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32);
+ }
+ if (!create_jobmedia_record(db, mjcr)) {
+ Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
+ dev->VolCatInfo.VolCatName, mjcr->Job);
+ }
+ }
+
+ fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
+ jcr->VolumeName, dev_name(dev));
+ getchar();
+ return 1;
+}
} else {
block->buf_len = dev->max_block_size;
}
- /* ****FIXME***** move this up to init_dev() */
- if (block->buf_len % TAPE_BSIZE != 0) {
- uint32_t old_len = block->buf_len;
- block->buf_len = ((old_len + TAPE_BSIZE - 1) / TAPE_BSIZE) * TAPE_BSIZE;
- Mmsg3(&dev->errmsg, _("Block size %u forced to %u to be multiple of %d\n"),
- old_len, block->buf_len, TAPE_BSIZE);
- Emsg0(M_WARNING, 0, dev->errmsg);
- dev->max_block_size = block->buf_len; /* force block size */
- }
block->block_len = block->buf_len; /* default block size */
block->buf = get_memory(block->buf_len);
if (block->buf == NULL) {
if (wlen < dev->min_block_size) {
wlen = ((dev->min_block_size + TAPE_BSIZE - 1) / TAPE_BSIZE) * TAPE_BSIZE;
}
+ /* check for fixed block size */
if (dev->min_block_size == dev->max_block_size) {
wlen = block->buf_len; /* fixed block size already rounded */
}
if (dev) {
dev->dev_errno = ENODEV;
}
- Emsg2(M_FATAL, 0, "%s is an unknown device type. Must be tape or directory. st_mode=%x\n",
+ Emsg2(M_FATAL, 0, _("%s is an unknown device type. Must be tape or directory. st_mode=%x\n"),
dev_name, statp.st_mode);
return NULL;
}
dev->max_open_wait = device->max_open_wait;
dev->device = device;
-
+ if (dev->max_block_size > 1000000) {
+ Emsg3(M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"),
+ dev->max_block_size, dev->dev_name, DEFAULT_BLOCK_SIZE);
+ dev->max_block_size = 0;
+ }
+ if (dev->max_block_size % TAPE_BSIZE != 0) {
+ Emsg2(M_WARNING, 0, _("Max block size %u not multiple of device %s block size.\n"),
+ dev->max_block_size, dev->dev_name);
+ }
+
dev->errmsg = get_pool_memory(PM_EMSG);
*dev->errmsg = 0;
if (!read_block_from_device(dev, block)) {
Dmsg0(20, "!read_record()\n");
if (dev->state & ST_EOT) {
- DEV_RECORD *record = new_record();
+ DEV_RECORD *trec = new_record();
Dmsg3(100, "EOT. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
block->BlockNumber, rec->remainder);
* be properly updated because this is the last
* tape.
*/
- record->FileIndex = EOT_LABEL;
- record->File = dev->file;
- record->Block = rec->Block; /* return block last read */
- record_cb(jcr, dev, block, record);
- free_record(record);
+ trec->FileIndex = EOT_LABEL;
+ trec->File = dev->file;
+ trec->Block = rec->Block; /* return block last read */
+ record_cb(jcr, dev, block, trec);
+ free_record(trec);
break;
}
Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
* most likely reading the previous record.
*/
read_block_from_device(dev, block);
- read_record_from_block(block, record);
- get_session_record(dev, record, &sessrec);
- record->File = dev->file;
- record_cb(jcr, dev, block, record);
- free_record(record);
+ read_record_from_block(block, trec);
+ get_session_record(dev, trec, &sessrec);
+ trec->File = dev->file;
+ record_cb(jcr, dev, block, trec);
+ free_record(trec);
goto next_record;
}
if (dev->state & ST_EOF) {
/* */
#define VERSION "1.27"
#define VSTRING "1"
-#define DATE "26 October 2002"
-#define LSMDATE "26Oct02"
+#define DATE "30 October 2002"
+#define LSMDATE "30Oct02"
/* Debug flags */
#define DEBUG 1