From de78564756d6e581f7f6c64cfb98d13561167cdc Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Wed, 30 Oct 2002 10:29:51 +0000 Subject: [PATCH] Minor changes git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@177 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/kernstodo | 63 ++++--- bacula/src/dird/dird_conf.c | 25 ++- bacula/src/dird/dird_conf.h | 212 ++++++++++++------------ bacula/src/dird/restore.c | 4 +- bacula/src/dird/ua_restore.c | 10 +- bacula/src/dird/ua_run.c | 55 ++++++- bacula/src/jcr.h | 1 + bacula/src/stored/.cvsignore | 1 + bacula/src/stored/Makefile.in | 31 ++-- bacula/src/stored/bcopy.c | 281 ++++++++++++++++++++++++++++++++ bacula/src/stored/block.c | 10 +- bacula/src/stored/dev.c | 13 +- bacula/src/stored/read_record.c | 22 +-- bacula/src/version.h | 4 +- 14 files changed, 540 insertions(+), 192 deletions(-) create mode 100644 bacula/src/stored/bcopy.c diff --git a/bacula/kernstodo b/bacula/kernstodo index ad94b20945..bd14c95fb6 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,45 +1,33 @@ 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. @@ -48,21 +36,14 @@ For 1.27 release: 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 @@ -72,7 +53,16 @@ For 1.27 release: 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 @@ -561,3 +551,8 @@ Done: (see kernsdone for more) - 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(). + diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index d737a7e08c..a5a4d12f1e 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -63,6 +63,7 @@ static void store_backup(LEX *lc, struct res_items *item, int index, int pass); 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 @@ -173,6 +174,7 @@ static struct res_items job_items[] = { {"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}, @@ -330,7 +332,7 @@ static struct s_kw RestoreFields[] = { }; /* Options permitted in Restore replace= */ -static struct s_kw ReplaceOptions[] = { +struct s_kw ReplaceOptions[] = { {"always", 'a'}, /* always */ {"ifnewer", 'w'}, {"never", 'n'}, @@ -990,7 +992,24 @@ static void store_level(LEX *lc, struct res_items *item, int index, int pass) 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 @@ -1170,7 +1189,7 @@ static void store_restore(LEX *lc, struct res_items *item, int index, int pass) /* 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; } diff --git a/bacula/src/dird/dird_conf.h b/bacula/src/dird/dird_conf.h index 22c38264a6..34e6944638 100644 --- a/bacula/src/dird/dird_conf.h +++ b/bacula/src/dird/dird_conf.h @@ -30,43 +30,43 @@ /* * 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 */ @@ -78,22 +78,22 @@ struct s_jt { /* 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; @@ -102,12 +102,12 @@ 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 */ @@ -119,15 +119,15 @@ typedef struct s_res_client CLIENT; * */ 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; @@ -136,9 +136,9 @@ 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; @@ -151,30 +151,30 @@ typedef struct s_res_cat CAT; * */ 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; @@ -183,7 +183,7 @@ typedef struct s_res_job JOB; * */ struct s_res_fs { - RES hdr; + RES hdr; char **include_array; int num_includes; @@ -191,9 +191,9 @@ struct s_res_fs { 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; @@ -203,7 +203,7 @@ typedef struct s_res_fs FILESET; * */ struct s_res_sch { - RES hdr; + RES hdr; struct s_run *run; }; @@ -214,7 +214,7 @@ typedef struct s_res_sch SCHED; * */ struct s_res_group { - RES hdr; + RES hdr; }; typedef struct s_res_group GROUP; @@ -222,12 +222,12 @@ 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; @@ -236,19 +236,19 @@ 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; @@ -257,16 +257,16 @@ 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; }; @@ -276,17 +276,17 @@ typedef union u_res URES; /* 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 */ diff --git a/bacula/src/dird/restore.c b/bacula/src/dird/restore.c index c276d96457..9c686b7656 100644 --- a/bacula/src/dird/restore.c +++ b/bacula/src/dird/restore.c @@ -230,8 +230,8 @@ int do_restore(JCR *jcr) /* 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 */ } diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 13ac6a99b9..c0a957821f 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -683,6 +683,7 @@ static int write_bsr_file(UAContext *ua, RBSR *bsr) FILE *fd; POOLMEM *fname = get_pool_memory(PM_MESSAGE); int stat; + RBSR *nbsr; Mmsg(&fname, "%s/restore.bsr", working_directory); fd = fopen(fname, "w+"); @@ -695,7 +696,14 @@ static int write_bsr_file(UAContext *ua, RBSR *bsr) 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; } diff --git a/bacula/src/dird/ua_run.c b/bacula/src/dird/ua_run.c index 98e998e73c..be33e03479 100644 --- a/bacula/src/dird/ua_run.c +++ b/bacula/src/dird/ua_run.c @@ -36,6 +36,7 @@ extern void run_job(JCR *jcr); /* Imported variables */ extern struct s_jl joblevels[]; +extern struct s_kw ReplaceOptions[]; /* * For Backup and Verify Jobs @@ -49,8 +50,8 @@ int runcmd(UAContext *ua, char *cmd) { 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; @@ -64,6 +65,7 @@ int runcmd(UAContext *ua, char *cmd) N_("storage"), N_("where"), N_("bootstrap"), + N_("replace"), NULL}; if (!open_db(ua)) { @@ -78,6 +80,7 @@ int runcmd(UAContext *ua, char *cmd) client_name = NULL; fileset_name = NULL; bootstrap = NULL; + replace = NULL; for (i=1; iargc; i++) { found = False; @@ -154,6 +157,14 @@ int runcmd(UAContext *ua, char *cmd) 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; } @@ -256,8 +267,28 @@ int runcmd(UAContext *ua, char *cmd) 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); @@ -326,6 +357,7 @@ Storage: %s\n"), JobName: %s\n\ Bootstrap: %s\n\ Where: %s\n\ +Replace: %s\n\ FileSet: %s\n\ Client: %s\n\ Storage: %s\n\ @@ -333,6 +365,7 @@ JobId: %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, @@ -367,7 +400,8 @@ JobId: %s\n"), 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: @@ -494,6 +528,17 @@ JobId: %s\n"), 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; diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index e7bfc436bc..368b39319a 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -141,6 +141,7 @@ struct s_jcr { 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 diff --git a/bacula/src/stored/.cvsignore b/bacula/src/stored/.cvsignore index 6802ea651b..7446154068 100644 --- a/bacula/src/stored/.cvsignore +++ b/bacula/src/stored/.cvsignore @@ -6,6 +6,7 @@ bextract bls bscan btape +bcopy btraceback btraceback.gdb file1Job1.bsr diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index 5216ca33c7..500f6a3569 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -52,14 +52,10 @@ SCNOBJS = bscan.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 - -# 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 @@ -77,16 +73,13 @@ FDLIBS=@FDLIBS@ .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 @@ -96,10 +89,11 @@ bls: ../findlib/libfind.a $(BLSOBJS) ../lib/libbac.a 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 @@ -110,7 +104,8 @@ install: all $(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 \ @@ -126,13 +121,15 @@ uninstall: (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 diff --git a/bacula/src/stored/bcopy.c b/bacula/src/stored/bcopy.c new file mode 100644 index 0000000000..e8265ded20 --- /dev/null +++ b/bacula/src/stored/bcopy.c @@ -0,0 +1,281 @@ +/* + * + * 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] \n" +" -b bootstrap specify a bootstrap file\n" +" -c 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; +} diff --git a/bacula/src/stored/block.c b/bacula/src/stored/block.c index 6d1df90b4d..8e1b01caaa 100644 --- a/bacula/src/stored/block.c +++ b/bacula/src/stored/block.c @@ -112,15 +112,6 @@ DEV_BLOCK *new_block(DEVICE *dev) } 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) { @@ -327,6 +318,7 @@ int write_block_to_dev(DEVICE *dev, DEV_BLOCK *block) 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 */ } diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 6a9ab69d1e..f1b62ab247 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -125,7 +125,7 @@ init_dev(DEVICE *dev, DEVRES *device) 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; } @@ -155,7 +155,16 @@ init_dev(DEVICE *dev, DEVRES *device) 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; diff --git a/bacula/src/stored/read_record.c b/bacula/src/stored/read_record.c index f747da67c2..b324bf01f8 100644 --- a/bacula/src/stored/read_record.c +++ b/bacula/src/stored/read_record.c @@ -56,7 +56,7 @@ int read_records(JCR *jcr, DEVICE *dev, 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); @@ -69,11 +69,11 @@ int read_records(JCR *jcr, DEVICE *dev, * 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), @@ -84,11 +84,11 @@ int read_records(JCR *jcr, DEVICE *dev, * 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) { diff --git a/bacula/src/version.h b/bacula/src/version.h index f5d7adfa77..b02e1cd68b 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #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 -- 2.39.5