Kern's ToDo List
- 12 December 2002
+ 16 December 2002
Documentation to do: (a little bit at a time)
- Document running a test version.
- that restore options work in FD (mostly done).
- that console command line options work
- blocksize recognition code.
-- Test new BSR code (mostly done).
-- Test watchdog child timer code.
-- Test that EndFile/Block are correctly updated at end of tape
- (in view of new block reading code).
-
For 1.28 release:
-- Count number of ignored messages in bscan and print when first
- SOS is found.
+- Implement FileOptions (see end of this document)
+- Make hash table for linked files in findlib/find_one.c:161
+- Make bcopy read through bad tape records.
- Need a verbose mode in restore, perhaps to bsr.
- Should we dump a SOS when starting a new tape?
- bscan without -v is too quiet -- perhaps show jobs.
- Figure out how to allow multiple simultaneous file Volumes on
a single device.
- Start working on Base jobs.
-- Implement FileOptions (see end of this document)
- Make sure the MaxVolFiles is fully implemented in SD
- Flush all the daemon messages at the end of every job.
- Check if both CatalogFiles and UseCatalog are set to SD.
- Check if we can increase Bacula FD priorty in Win2000
-- Make bcopy read through bad tape records.
- Need return status on read_cb() from read_records(). Need multiple
records -- one per Job, maybe a JCR or some other structure with
a block and a record.
-- Look at hierarchial storage for Bacula.
- Implement Bacula plugins -- design API
- Work more on how to to a Bacula restore beginning with
This could be the output of df; or perhaps some sort of /etc/mtab record.
Longer term to do:
+- Design at hierarchial storage for Bacula.
- Implement FSM (File System Modules).
- Identify unchanged or "system" files and save them to a
special tape thus removing them from the standard
to let user know it is working.
- DateWritten field on tape may be wrong.
- Ensure that restore of differential jobs works (check SQL).
-
+- Count number of ignored messages in bscan and print when first SOS is found.
+- Test that EndFile/Block are correctly updated at end of tape
+ (in view of new block reading code).
+- Test watchdog child timer code.
+- Test new BSR code (mostly done).
+
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);
+static void store_fo(LEX *lc, struct res_items *item, int index, int pass);
+static void store_applyto(LEX *lc, struct res_items *item, int index, int pass);
+
+/* Keywords (RHS) permitted in Job Level records
+ *
+ * level_name level job_type
+ */
+struct s_jl joblevels[] = {
+ {"Full", L_FULL, JT_BACKUP},
+ {"Incremental", L_INCREMENTAL, JT_BACKUP},
+ {"Differential", L_DIFFERENTIAL, JT_BACKUP},
+ {"Since", L_SINCE, JT_BACKUP},
+ {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
+ {"Initcatalog", L_VERIFY_INIT, JT_VERIFY},
+ {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
+ {"Data", L_VERIFY_DATA, JT_VERIFY},
+ {NULL, 0}
+};
+
+/* Keywords (RHS) permitted in Job type records
+ *
+ * type_name job_type
+ */
+struct s_jt jobtypes[] = {
+ {"backup", JT_BACKUP},
+ {"admin", JT_ADMIN},
+ {"verify", JT_VERIFY},
+ {"restore", JT_RESTORE},
+ {NULL, 0}
+};
+
+
+/* Keywords (RHS) permitted in Backup and Verify records */
+static struct s_kw BakVerFields[] = {
+ {"client", 'C'},
+ {"fileset", 'F'},
+ {"level", 'L'},
+ {NULL, 0}
+};
+
+/* Keywords (RHS) permitted in Restore records */
+static struct s_kw RestoreFields[] = {
+ {"client", 'C'},
+ {"fileset", 'F'},
+ {"jobid", 'J'}, /* JobId to restore */
+ {"where", 'W'}, /* root of restore */
+ {"replace", 'R'}, /* replacement options */
+ {"bootstrap", 'B'}, /* bootstrap file */
+ {NULL, 0}
+};
+
+/* Options permitted in Restore replace= */
+struct s_kw ReplaceOptions[] = {
+ {"always", REPLACE_ALWAYS},
+ {"ifnewer", REPLACE_IFNEWER},
+ {"ifolder", REPLACE_IFOLDER},
+ {"never", REPLACE_NEVER},
+ {NULL, 0}
+};
+
+
+
+/* Define FileSet KeyWord values */
+
+#define INC_KW_NONE 0
+#define INC_KW_COMPRESSION 1
+#define INC_KW_SIGNATURE 2
+#define INC_KW_ENCRYPTION 3
+#define INC_KW_VERIFY 4
+#define INC_KW_ONEFS 5
+#define INC_KW_RECURSE 6
+#define INC_KW_SPARSE 7
+#define INC_KW_REPLACE 8 /* restore options */
+
+/* Include keywords */
+static struct s_kw FS_option_kw[] = {
+ {"compression", INC_KW_COMPRESSION},
+ {"signature", INC_KW_SIGNATURE},
+ {"encryption", INC_KW_ENCRYPTION},
+ {"verify", INC_KW_VERIFY},
+ {"onefs", INC_KW_ONEFS},
+ {"recurse", INC_KW_RECURSE},
+ {"sparse", INC_KW_SPARSE},
+ {"replace", INC_KW_REPLACE},
+ {NULL, 0}
+};
+
+/* Options for FileSet keywords */
+
+struct s_fs_opt {
+ char *name;
+ int keyword;
+ char *option;
+};
+
+/* Options permitted for each keyword and resulting value */
+static struct s_fs_opt FS_options[] = {
+ {"md5", INC_KW_SIGNATURE, "M"},
+ {"gzip", INC_KW_COMPRESSION, "Z6"},
+ {"gzip1", INC_KW_COMPRESSION, "Z1"},
+ {"gzip2", INC_KW_COMPRESSION, "Z2"},
+ {"gzip3", INC_KW_COMPRESSION, "Z3"},
+ {"gzip4", INC_KW_COMPRESSION, "Z4"},
+ {"gzip5", INC_KW_COMPRESSION, "Z5"},
+ {"gzip6", INC_KW_COMPRESSION, "Z6"},
+ {"gzip7", INC_KW_COMPRESSION, "Z7"},
+ {"gzip8", INC_KW_COMPRESSION, "Z8"},
+ {"gzip9", INC_KW_COMPRESSION, "Z9"},
+ {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
+ {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
+ {"yes", INC_KW_ONEFS, "0"},
+ {"no", INC_KW_ONEFS, "f"},
+ {"yes", INC_KW_RECURSE, "0"},
+ {"no", INC_KW_RECURSE, "h"},
+ {"yes", INC_KW_SPARSE, "s"},
+ {"no", INC_KW_SPARSE, "0"},
+ {"always", INC_KW_REPLACE, "a"},
+ {"ifnewer", INC_KW_REPLACE, "w"},
+ {"never", INC_KW_REPLACE, "n"},
+ {NULL, 0, 0}
+};
/* We build the current resource here as we are
{NULL, NULL, NULL, 0, 0, 0}
};
+/*
+ * FileOptions resource (options for Include)
+ *
+ */
+static struct res_items fo_items[] = {
+ {"name", store_name, ITEM(res_fo.hdr.name), 0, ITEM_REQUIRED, 0},
+ {"description", store_str, ITEM(res_fo.hdr.desc), 0, 0, 0},
+ {"compression", store_fo, ITEM(res_fo.opts), INC_KW_COMPRESSION, 0, 0},
+ {"signature", store_fo, ITEM(res_fo.opts), INC_KW_SIGNATURE, 0, 0},
+ {"encryption", store_fo, ITEM(res_fo.opts), INC_KW_ENCRYPTION, 0, 0},
+ {"onefs", store_fo, ITEM(res_fo.opts), INC_KW_ONEFS, 0, 0},
+ {"recurse", store_fo, ITEM(res_fo.opts), INC_KW_RECURSE, 0, 0},
+ {"sparse", store_fo, ITEM(res_fo.opts), INC_KW_SPARSE, 0, 0},
+ {"replace", store_fo, ITEM(res_fo.opts), INC_KW_REPLACE, 0, 0},
+ {"verify", store_fo, ITEM(res_fo.opts), INC_KW_VERIFY, 0, 0},
+ {"applyto", store_applyto, ITEM(res_fo.applyto), 0, 0, 0},
+ {NULL, NULL, NULL, 0, 0, 0}
+};
+
+
/* Schedule -- see run_conf.c */
/* Schedule
*
{"pool", pool_items, R_POOL, NULL},
{"messages", msgs_items, R_MSGS, NULL},
{"counter", counter_items, R_COUNTER, NULL},
+ {"fileoptions", fo_items, R_FILEOPTIONS, NULL},
{NULL, NULL, 0, NULL}
};
-/* Keywords (RHS) permitted in Job Level records
- *
- * level_name level job_type
- */
-struct s_jl joblevels[] = {
- {"Full", L_FULL, JT_BACKUP},
- {"Incremental", L_INCREMENTAL, JT_BACKUP},
- {"Differential", L_DIFFERENTIAL, JT_BACKUP},
- {"Level", L_LEVEL, JT_BACKUP},
- {"Since", L_SINCE, JT_BACKUP},
- {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
- {"Initcatalog", L_VERIFY_INIT, JT_VERIFY},
- {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
- {"Data", L_VERIFY_DATA, JT_VERIFY},
- {NULL, 0}
-};
-
-/* Keywords (RHS) permitted in Job type records
- *
- * type_name job_type
- */
-struct s_jt jobtypes[] = {
- {"backup", JT_BACKUP},
- {"admin", JT_ADMIN},
- {"verify", JT_VERIFY},
- {"restore", JT_RESTORE},
- {NULL, 0}
-};
-
-
-/* Keywords (RHS) permitted in Backup and Verify records */
-static struct s_kw BakVerFields[] = {
- {"client", 'C'},
- {"fileset", 'F'},
- {"level", 'L'},
- {NULL, 0}
-};
-
-/* Keywords (RHS) permitted in Restore records */
-static struct s_kw RestoreFields[] = {
- {"client", 'C'},
- {"fileset", 'F'},
- {"jobid", 'J'}, /* JobId to restore */
- {"where", 'W'}, /* root of restore */
- {"replace", 'R'}, /* replacement options */
- {"bootstrap", 'B'}, /* bootstrap file */
- {NULL, 0}
-};
-
-/* Options permitted in Restore replace= */
-struct s_kw ReplaceOptions[] = {
- {"always", REPLACE_ALWAYS},
- {"ifnewer", REPLACE_IFNEWER},
- {"ifolder", REPLACE_IFOLDER},
- {"never", REPLACE_NEVER},
- {NULL, 0}
-};
-
-
-
-/* Define FileSet KeyWord values */
-
-#define INC_KW_NONE 0
-#define INC_KW_COMPRESSION 1
-#define INC_KW_SIGNATURE 2
-#define INC_KW_ENCRYPTION 3
-#define INC_KW_VERIFY 4
-#define INC_KW_ONEFS 5
-#define INC_KW_RECURSE 6
-#define INC_KW_SPARSE 7
-#define INC_KW_REPLACE 8 /* restore options */
-
-/* Include keywords */
-static struct s_kw FS_option_kw[] = {
- {"compression", INC_KW_COMPRESSION},
- {"signature", INC_KW_SIGNATURE},
- {"encryption", INC_KW_ENCRYPTION},
- {"verify", INC_KW_VERIFY},
- {"onefs", INC_KW_ONEFS},
- {"recurse", INC_KW_RECURSE},
- {"sparse", INC_KW_SPARSE},
- {"replace", INC_KW_REPLACE},
- {NULL, 0}
-};
-
-/* Options for FileSet keywords */
-
-struct s_fs_opt {
- char *name;
- int keyword;
- char *option;
-};
-
-/* Options permitted for each keyword and resulting value */
-static struct s_fs_opt FS_options[] = {
- {"md5", INC_KW_SIGNATURE, "M"},
- {"gzip", INC_KW_COMPRESSION, "Z6"},
- {"gzip1", INC_KW_COMPRESSION, "Z1"},
- {"gzip2", INC_KW_COMPRESSION, "Z2"},
- {"gzip3", INC_KW_COMPRESSION, "Z3"},
- {"gzip4", INC_KW_COMPRESSION, "Z4"},
- {"gzip5", INC_KW_COMPRESSION, "Z5"},
- {"gzip6", INC_KW_COMPRESSION, "Z6"},
- {"gzip7", INC_KW_COMPRESSION, "Z7"},
- {"gzip8", INC_KW_COMPRESSION, "Z8"},
- {"gzip9", INC_KW_COMPRESSION, "Z9"},
- {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
- {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
- {"yes", INC_KW_ONEFS, "0"},
- {"no", INC_KW_ONEFS, "f"},
- {"yes", INC_KW_RECURSE, "0"},
- {"no", INC_KW_RECURSE, "h"},
- {"yes", INC_KW_SPARSE, "s"},
- {"no", INC_KW_SPARSE, "0"},
- {"always", INC_KW_REPLACE, "a"},
- {"ifnewer", INC_KW_REPLACE, "w"},
- {"never", INC_KW_REPLACE, "n"},
- {NULL, 0, 0}
-};
char *level_to_str(int level)
{
lc->options = options;
set_bit(index, res_all.hdr.item_present);
}
+
+/* Store FileOptions */
+static void store_fo(LEX *lc, struct res_items *item, int index, int pass)
+{
+ int token, i;
+ int options = lc->options;
+ int keyword;
+ char inc_opts[100];
+ int inc_opts_len;
+
+ lc->options |= LOPT_NO_IDENT; /* make spaces significant */
+
+ /* Get include options */
+ strcpy(inc_opts, "0"); /* set no options */
+ while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
+ keyword = INC_KW_NONE;
+ for (i=0; FS_option_kw[i].name; i++) {
+ if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
+ keyword = FS_option_kw[i].token;
+ break;
+ }
+ }
+ if (keyword == INC_KW_NONE) {
+ scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
+ }
+ /* Option keyword should be following by = <option> */
+ if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
+ scan_err1(lc, "expected an = following keyword, got: %s", lc->str);
+ }
+ scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
+ if (token == T_BOB) {
+ break;
+ }
+ }
+ strcat(inc_opts, " "); /* add field separator */
+ inc_opts_len = strlen(inc_opts);
+
+
+ if (pass == 1) {
+ if (!res_all.res_fs.have_MD5) {
+ MD5Init(&res_all.res_fs.md5c);
+ res_all.res_fs.have_MD5 = TRUE;
+ }
+ } else { /* pass 2 */
+ while (lex_get_token(lc, T_ALL) != T_EOB)
+ {}
+ }
+ scan_to_eol(lc);
+ lc->options = options;
+ set_bit(index, res_all.hdr.item_present);
+}
+
+/* Store FileOptions */
+static void store_applyto(LEX *lc, struct res_items *item, int index, int pass)
+{
+ scan_to_eol(lc);
+}
/*
* 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_FILEOPTIONS 1012
+
+#define R_LAST R_FILEOPTIONS
/*
* 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;
- utime_t FDConnectTimeout; /* timeout for connect in seconds */
- utime_t SDConnectTimeout; /* timeout in seconds */
+ int MaxConcurrentJobs;
+ utime_t FDConnectTimeout; /* timeout for connect in seconds */
+ utime_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? */
- utime_t FileRetention; /* file retention period in seconds */
- utime_t JobRetention; /* job retention period in seconds */
+ int FDport; /* Where File daemon listens */
+ int AutoPrune; /* Do automatic pruning? */
+ utime_t FileRetention; /* file retention period in seconds */
+ utime_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 replace; /* How (overwrite, ..) */
- utime_t MaxRunTime; /* max run time in seconds */
- utime_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, ..) */
+ utime_t MaxRunTime; /* max run time in seconds */
+ utime_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;
+
+/*
+ * FileOptions Resource (options for Includes)
+ */
+struct s_res_fo {
+ RES hdr;
+
+ char opts[50]; /* Options string */
+ struct s_applyto *applyto; /* applyto strings */
+};
+typedef struct s_res_fo FILEOPTIONS;
/*
*
*/
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 */
- uint32_t max_volumes; /* max number of volumes */
- utime_t VolRetention; /* volume retention period in seconds */
- utime_t VolUseDuration; /* duration volume can be used */
- uint32_t MaxVolJobs; /* Maximum jobs on the Volume */
- uint32_t MaxVolFiles; /* Maximum files on the Volume */
- uint64_t MaxVolBytes; /* Maximum bytes on the Volume */
- 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 */
+ uint32_t max_volumes; /* max number of volumes */
+ utime_t VolRetention; /* volume retention period in seconds */
+ utime_t VolUseDuration; /* duration volume can be used */
+ uint32_t MaxVolJobs; /* Maximum jobs on the Volume */
+ uint32_t MaxVolFiles; /* Maximum files on the Volume */
+ uint64_t MaxVolBytes; /* Maximum bytes on the Volume */
+ 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;
+ struct s_res_fo res_fo;
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 */
jcr->client->hdr.name,
sdt,
edt,
- edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
+ edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec1),
edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
(float)kbps,
term_msg);
add_prompt(ua, _("Full"));
add_prompt(ua, _("Incremental"));
add_prompt(ua, _("Differential"));
- add_prompt(ua, _("Level"));
add_prompt(ua, _("Since"));
switch (do_prompt(ua, _("Select level"), NULL, 0)) {
case 0:
jcr->JobLevel = L_DIFFERENTIAL;
break;
case 3:
- jcr->JobLevel = L_LEVEL;
- break;
- case 4:
jcr->JobLevel = L_SINCE;
break;
default:
#define L_FULL 'F'
#define L_INCREMENTAL 'I' /* since last backup */
#define L_DIFFERENTIAL 'D' /* since last full backup */
-#define L_LEVEL 'L'
#define L_SINCE 'S'
#define L_VERIFY_CATALOG 'C' /* verify from catalog */
#define L_VERIFY_INIT 'V' /* verify save (init DB) */
case L_DIFFERENTIAL:
str = _("Differential");
break;
- case L_LEVEL:
- str = _("Level");
- break;
case L_SINCE:
str = _("Since");
break;
} else if (dev->num_writers > 0) {
dev->num_writers--;
+ if (dev->state & ST_TAPE) {
+ jcr->EndBlock = dev->EndBlock;
+ jcr->EndFile = dev->EndFile;
+ Dmsg2(000, "Release device: EndFile=%u EndBlock=%u\n", jcr->EndFile, jcr->EndBlock);
+ } else {
+ jcr->EndBlock = (uint32_t)dev->file_addr;
+ jcr->EndFile = (uint32_t)(dev->file_addr >> 32);
+ }
Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
if (dev->num_writers == 0) {
- weof_dev(dev, 1);
Dmsg0(100, "dir_create_jobmedia_record. Release\n");
dir_create_jobmedia_record(jcr);
+ weof_dev(dev, 1);
dev->VolCatInfo.VolCatFiles++; /* increment number of files */
dev->VolCatInfo.VolCatJobs++; /* increment number of jobs */
/* Note! do volume update before close, which zaps VolCatInfo */
char Id[BLKHDR_ID_LENGTH+1];
uint32_t CheckSum, BlockCheckSum;
uint32_t block_len;
- uint32_t EndBlock;
+ uint32_t block_end;
uint32_t BlockNumber;
int bhl;
Dmsg1(190, "unser_block_header block_len=%d\n", block_len);
/* Find end of block or end of buffer whichever is smaller */
if (block_len > block->read_len) {
- EndBlock = block->read_len;
+ block_end = block->read_len;
} else {
- EndBlock = block_len;
+ block_end = block_len;
}
- block->binbuf = EndBlock - bhl;
+ block->binbuf = block_end - bhl;
block->block_len = block_len;
block->BlockNumber = BlockNumber;
Dmsg3(190, "Read binbuf = %d %d block_len=%d\n", block->binbuf,
edit_uint64(dev->VolCatInfo.VolCatMaxBytes, ed1), dev->dev_name);
}
block->failed_write = TRUE;
+ dev->EndBlock = dev->block_num;
+ dev->EndFile = dev->file;
weof_dev(dev, 1); /* end the tape */
weof_dev(dev, 1); /* write second eof */
return 0;
Mmsg4(&dev->errmsg, _("Write error on device %s. Write of %u bytes got %d. ERR=%s.\n"),
dev->dev_name, wlen, stat, strerror(dev->dev_errno));
block->failed_write = TRUE;
+ dev->EndBlock = dev->block_num;
+ dev->EndFile = dev->file;
weof_dev(dev, 1); /* end the tape */
weof_dev(dev, 1); /* write second eof */
* correct.
*/
if (dev->state & ST_TAPE && dev->capabilities & CAP_BSR) {
- uint32_t file, block_num;
- file = dev->file;
- block_num = dev->block_num;
-
/* Now back up over what we wrote and read the last block */
if (bsf_dev(dev, 1) != 0 || bsf_dev(dev, 1) != 0) {
ok = FALSE;
}
free_block(lblock);
}
- dev->file = file;
- dev->block_num = block_num;
}
#endif
return 0;
dev->VolCatInfo.VolCatBytes += block->binbuf;
dev->VolCatInfo.VolCatBlocks++;
dev->file_addr += wlen;
+ dev->EndBlock = dev->block_num;
+ dev->EndFile = dev->file;
/* Limit maximum File size on volume to user specified value */
if (dev->state & ST_TAPE) {
static int update_db = 0;
static int update_vol_info = 0;
static int list_records = 0;
+static int ignored_msgs = 0;
#define CONFIG_FILE "bacula-sd.conf"
char *configfile;
break;
case SOS_LABEL:
mr.VolJobs++;
+ if (ignored_msgs > 0) {
+ Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
+ ignored_msgs);
+ ignored_msgs = 0;
+ }
unser_session_label(&label, rec);
memset(&jr, 0, sizeof(jr));
jr.JobId = label.JobId;
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Attributes record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return;
}
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for File Data record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return;
}
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse Data record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return;
}
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for GZIP Data record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return;
}
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse GZIP Data record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return;
}
JOBMEDIA_DBR jmr;
if (dev->state & ST_TAPE) {
- mjcr->EndBlock = dev->block_num;
- mjcr->EndFile = dev->file;
+ mjcr->EndBlock = dev->EndBlock;
+ mjcr->EndFile = dev->EndFile;
} else {
mjcr->EndBlock = (uint32_t)dev->file_addr;
mjcr->EndFile = (uint32_t)(dev->file_addr >> 32);
if (mr.VolJobs > 0) {
Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5 record.\n"),
rec->VolSessionId, rec->VolSessionTime);
+ } else {
+ ignored_msgs++;
}
return 0;
}
Pmsg1(000, "create JobMedia for Job %s\n", mjcr->Job);
}
if (dev->state & ST_TAPE) {
- mjcr->EndBlock = dev->block_num;
- mjcr->EndFile = dev->file;
+ mjcr->EndBlock = dev->EndBlock;
+ mjcr->EndFile = dev->EndFile;
} else {
mjcr->EndBlock = (uint32_t)dev->file_addr;
mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32);
static uint32_t eot_block_len;
static uint32_t eot_FileIndex;
static int dumped = 0;
+static DEV_BLOCK *last_block = NULL;
+static DEV_BLOCK *this_block = NULL;
+static uint32_t last_file = 0;
+static uint32_t last_block_num = 0;
+static int simple = TRUE;
static char *VolumeName = NULL;
free_bsr(bsr);
}
+ if (last_block) {
+ free_block(last_block);
+ }
+ if (this_block) {
+ free_block(this_block);
+ }
+
term_msg();
close_memory_pool(); /* free memory in pool */
return 1;
}
-
-/*
- * This is a general test of Bacula's functions
- * needed to read and write the tape.
- */
-static void testcmd()
+void append_block_test()
{
- re_read_block_test();
-
- if (!append_test()) {
- return;
- }
-
Pmsg0(-1, "\n\n=== Append block test. ===\n\n\
I'm going to write a block, an EOF, rewind, go to EOM,\n\
then backspace over the EOF and attempt to append a second\n\
}
-static void fsfcmd()
+
+/*
+ * This is a general test of Bacula's functions
+ * needed to read and write the tape.
+ */
+static void testcmd()
{
- int stat;
+ re_read_block_test();
+
+ if (!append_test()) {
+ return;
+ }
+
+ append_block_test();
+
+
+}
- if ((stat=fsf_dev(dev, 1)) < 0) {
- Pmsg2(0, "Bad status from fsf %d. ERR=%s\n", stat, strerror_dev(dev));
+/* Forward space a file */
+static void fsfcmd()
+{
+ if (!fsf_dev(dev, 1)) {
+ Pmsg1(0, "Bad status from fsf. ERR=%s\n", strerror_dev(dev));
return;
}
Pmsg0(0, "Forward spaced one file.\n");
}
+/* Forward space a record */
static void fsrcmd()
{
int stat;
Pmsg0(0, "Forward spaced one record.\n");
}
+/* DEPRECATED DO NOT USE */
static void rdcmd()
{
#ifdef xxxxx
char *p;
ok = TRUE;
- stop = FALSE;
+ stop = 0;
Pmsg0(000, "\n\
This command simulates Bacula writing to a tape.\n\
-It command requires two blank tapes, which it\n\
+It requires either one or two blank tapes, which it\n\
will label and write. It will print a status approximately\n\
-every 322 MB, and write an EOF every 3.2 GB. When the first tape\n\
+every 322 MB, and write an EOF every 3.2 GB. If you have\n\
+selected the simple test option, after writing the first tape\n\
+it will rewind it and re-read the last block written.\n\
+If you have selected the multiple tape test, when the first tape\n\
fills, it will ask for a second, and after writing a few \n\
blocks, it will stop. Then it will begin re-reading the\n\
This may take a long time. I.e. hours! ...\n\n");
- get_cmd("Insert a blank tape then answer. Do you wish to continue? (y/n): ");
- if (cmd[0] != 'y') {
+ get_cmd("Insert a blank tape then indicate if you want\n"
+ "to run the simplified test (s) with one tape or\n"
+ "the complete multiple tape (m) test: (s/m) ");
+ if (cmd[0] == 's') {
+ Pmsg0(-1, "Simple test (single tape) selected.\n");
+ simple = TRUE;
+ } else if (cmd[0] == 'm') {
+ Pmsg0(-1, "Complete multiple tape test selected.\n");
+ simple = FALSE;
+ } else {
Pmsg0(000, "Command aborted.\n");
return;
}
rec.VolSessionTime = jcr->VolSessionTime;
rec.FileIndex = ++file_index;
rec.Stream = STREAM_FILE_DATA;
- /* Write file_index at beginning of buffer */
+
+ /* Write file_index at beginning of buffer and add file_index to each
+ * uint64_t item to make it unique.
+ */
lp = (uint64_t *)rec.data;
- *lp = (uint64_t)file_index;
+ *lp++ = (uint64_t)file_index;
+ for (uint32_t i=0; i < (REC_SIZE-sizeof(uint64_t))/sizeof(uint64_t); i++) {
+ *lp++ = *lp + rec.FileIndex;
+ }
Dmsg4(250, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream, rec.FileIndex),
/* The weof resets the block number */
}
- if (block->BlockNumber > 10 && stop) { /* get out */
+ if (block->BlockNumber > 10 && stop != 0) { /* get out */
break;
}
}
if (!ok) {
- Pmsg0(000, "Not OK\n");
+ Pmsg0(000, _("Not OK\n"));
break;
}
jcr->JobBytes += rec.data_len; /* increment bytes this job */
FI_to_ascii(rec.FileIndex), rec.VolSessionId,
stream_to_ascii(rec.Stream, rec.FileIndex), rec.data_len);
}
- Dmsg0(000, "Write_end_session_label()\n");
- /* Create Job status for end of session label */
- if (!job_cancelled(jcr) && ok) {
- jcr->JobStatus = JS_Terminated;
- } else if (!ok) {
- jcr->JobStatus = JS_ErrorTerminated;
- }
- if (!write_session_label(jcr, block, EOS_LABEL)) {
- Pmsg1(000, _("Error writting end session label. ERR=%s\n"), strerror_dev(dev));
- ok = FALSE;
- }
- /* Write out final block of this session */
- if (!write_block_to_device(jcr, dev, block)) {
- Pmsg0(000, "Set ok=FALSE after write_block_to_device.\n");
- ok = FALSE;
+ if (stop > 0) {
+ Dmsg0(000, "Write_end_session_label()\n");
+ /* Create Job status for end of session label */
+ if (!job_cancelled(jcr) && ok) {
+ jcr->JobStatus = JS_Terminated;
+ } else if (!ok) {
+ jcr->JobStatus = JS_ErrorTerminated;
+ }
+ if (!write_session_label(jcr, block, EOS_LABEL)) {
+ Pmsg1(000, _("Error writting end session label. ERR=%s\n"), strerror_dev(dev));
+ ok = FALSE;
+ }
+ /* Write out final block of this session */
+ if (!write_block_to_device(jcr, dev, block)) {
+ Pmsg0(000, _("Set ok=FALSE after write_block_to_device.\n"));
+ ok = FALSE;
+ }
}
/* Release the device */
if (!release_device(jcr, dev)) {
- Pmsg0(000, "Error in release_device\n");
+ Pmsg0(000, _("Error in release_device\n"));
ok = FALSE;
}
free_block(block);
free_memory(rec.data);
- Pmsg0(000, "Done with fill command. Now beginning re-read of tapes...\n");
+ Pmsg0(000, _("\n\nDone filling tape. Now beginning re-read of tape ...\n"));
+
+ dump_block(last_block, _("Last block written to tape.\n"));
unfillcmd();
}
/*
* Read two tapes written by the "fill" command and ensure
- * that the data is valid.
+ * that the data is valid. If stop==1 we simulate full read back
+ * of two tapes. If stop==-1 we simply read the last block and
+ * verify that it is correct.
*/
static void unfillcmd()
{
dev->capabilities &= ~CAP_LABEL; /* don't label anything here */
end_of_tape = 0;
- get_cmd("Mount first of two tapes. Press enter when ready: ");
+ if (!simple) {
+ get_cmd(_("Mount first of two tapes. Press enter when ready: "));
+ }
free_vol_list(jcr);
pm_strcpy(&jcr->VolumeName, "TestVolume1");
time(&jcr->run_time); /* start counting time for rates */
stop = 0;
file_index = 0;
- read_records(jcr, dev, record_cb, my_mount_next_read_volume);
+ if (!simple) {
+ /* Read all records and then second tape */
+ read_records(jcr, dev, record_cb, my_mount_next_read_volume);
+ } else {
+ /*
+ * Simplified test, we simply fsf to file, then read the
+ * last block and make sure it is the same as the saved block.
+ */
+ if (!rewind_dev(dev)) {
+ Pmsg1(-1, _("Error rewinding: ERR=%s\n"), strerror_dev(dev));
+ goto bail_out;
+ }
+ if (last_file > 0) {
+ if (!fsf_dev(dev, last_file)) {
+ Pmsg1(-1, _("Error in FSF: ERR=%s\n"), strerror_dev(dev));
+ goto bail_out;
+ }
+ }
+ Pmsg1(-1, _("Forward space to file %u complete. Reading blocks ...\n"),
+ last_file);
+ Pmsg1(-1, _("Now reading to block %u.\n"), last_block_num);
+ for (uint32_t i= 0; i < last_block_num; i++) {
+ if (!read_block_from_device(dev, block)) {
+ Pmsg1(-1, _("Error reading blocks: ERR=%s\n"), strerror_dev(dev));
+ Pmsg2(-1, _("Wanted block %u error at block %u\n"), last_block_num, i);
+ goto bail_out;
+ }
+ if (i > 0 && i % 1000 == 0) {
+ Pmsg1(-1, _("At block %u\n"), i);
+ }
+ }
+ dump_block(last_block, _("Last block written"));
+ dump_block(block, _("Block read back"));
+ Pmsg0(-1, _("Except for the buffer address, the contents of\n"
+ "the above two block dumps should be the same.\n"
+ "If not you have a problem ...\n"));
+ }
+
+bail_out:
free_block(block);
- Pmsg0(000, "Done with unfillcmd.\n");
+ Pmsg0(000, _("Done with reread of fill data.\n"));
}
{
char ec1[50];
lock_device(dev);
+ DEV_BLOCK *tblock;
+ uint32_t this_file, this_block_num;
+
+ if (!this_block) {
+ this_block = new_block(dev);
+ }
+ /* Copy block */
+ memcpy(this_block, block, sizeof(DEV_BLOCK));
+ if (this_block->buf_len < block->buf_len) {
+ free_memory(this_block->buf);
+ this_block->buf = get_memory(block->buf_len);
+ this_block->buf_len = block->buf_len;
+ }
+ memcpy(this_block->buf, block->buf, this_block->buf_len);
+ this_file = dev->file;
+ this_block_num = dev->block_num;
if (!write_block_to_dev(jcr, dev, block)) {
Pmsg0(000, strerror_dev(dev));
Pmsg3(000, "Block not written: FileIndex=%u Block=%u Size=%u\n",
if (dump) {
dump_block(block, "Block not written");
}
- if (!stop) {
+ if (stop == 0) {
eot_block = block->BlockNumber;
eot_block_len = block->block_len;
eot_FileIndex = file_index;
vol_size = dev->VolCatInfo.VolCatBytes;
Pmsg2(000, "End of tape. VolumeCapacity=%s. Write rate = %.1f KB/s\n",
edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs);
- if (!fixup_device_block_write_error(jcr, dev, block)) {
- Pmsg1(000, _("Cannot fixup device error. %s\n"), strerror_dev(dev));
- ok = FALSE;
- unlock_device(dev);
- return 0;
+
+ if (simple) {
+ stop = -1; /* stop, but do simplified test */
+ } else {
+ /* Full test in progress */
+ if (!fixup_device_block_write_error(jcr, dev, block)) {
+ Pmsg1(000, _("Cannot fixup device error. %s\n"), strerror_dev(dev));
+ ok = FALSE;
+ unlock_device(dev);
+ return 0;
+ }
+ stop = 1;
}
- stop = 1;
unlock_device(dev);
- return 1; /* write one more block to next tape then stop */
+ return 1; /* end of tape reached */
}
+
+ /*
+ * Toggle between two allocated blocks for efficiency.
+ * Switch blocks so that the block just successfully written is
+ * always in last_block.
+ */
+ tblock = last_block;
+ last_block = this_block;
+ this_block = tblock;
+ last_file = this_file;
+ last_block_num = this_block_num;
+
unlock_device(dev);
return 1;
}
}
while (!(dev->state & ST_EOT)) {
Dmsg0(200, "Do fsf 1\n");
- if (fsf_dev(dev, 1) < 0) {
- Dmsg0(200, "fsf_dev return < 0\n");
+ if (!fsf_dev(dev, 1)) {
+ Dmsg0(200, "fsf_dev error.\n");
return 0;
}
}
/*
* Foward space a file
+ * Returns: 1 on success
+ * 0 on failure
*/
int
fsf_dev(DEVICE *dev, int num)
dev->dev_errno = EBADF;
Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
- return -1;
+ return 0;
}
if (!(dev->state & ST_TAPE)) {
- return 0;
+ return 1;
}
if (dev->state & ST_EOT) {
dev->dev_errno = 0;
Mmsg1(&dev->errmsg, _("Device %s at End of Tape.\n"), dev->dev_name);
- return -1;
+ return 0;
}
if (dev->state & ST_EOF)
Dmsg0(200, "ST_EOF set on entry to FSF\n");
if (dev->state & ST_EOT)
Dmsg0(200, "ST_EOT set on exit FSF\n");
Dmsg1(200, "Return from FSF file=%d\n", dev->file);
- return stat;
+ return stat == 0 ? 1 : 0;
}
/*
/* Clean up device packet so it can be reused */
dev->fd = -1;
dev->state &= ~(ST_OPENED|ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF);
- dev->block_num = 0;
- dev->file = 0;
+ dev->file = dev->block_num = 0;
dev->file_addr = 0;
- dev->LastBlockNumWritten = 0;
+ dev->EndFile = dev->EndBlock = 0;
memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
dev->use_count--;
uint32_t block_num; /* current block number base 0 */
uint32_t file; /* current file number base 0 */
uint64_t file_addr; /* Current file read/write address */
- uint32_t LastBlockNumWritten; /* last block written */
+ uint32_t EndBlock; /* last block written */
+ uint32_t EndFile; /* last file written */
uint32_t min_block_size; /* min block size */
uint32_t max_block_size; /* max block size */
uint32_t max_volume_jobs; /* max jobs to put on one volume */
for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
Dmsg1(100, "create JobMedia for Job %s\n", mjcr->Job);
if (dev->state & ST_TAPE) {
- mjcr->EndBlock = dev->block_num;
- mjcr->EndFile = dev->file;
+ mjcr->EndBlock = dev->EndBlock;
+ mjcr->EndFile = dev->EndFile;
+ Dmsg2(000, "Fixup EndFile=%u EndBlock=%u\n", mjcr->EndFile, mjcr->EndBlock);
} else {
mjcr->EndBlock = (uint32_t)dev->file_addr;
mjcr->EndFile = (uint32_t)(dev->file_addr >> 32);
dev->VolHdr.label_time = dt.julian_day_fraction;
}
+ if (gethostname(dev->VolHdr.HostName, sizeof(dev->VolHdr.HostName)) != 0) {
+ dev->VolHdr.HostName[0] = 0;
+ }
bstrncpy(dev->VolHdr.LabelProg, my_name, sizeof(dev->VolHdr.LabelProg));
sprintf(dev->VolHdr.ProgVersion, "Ver. %s %s", VERSION, DATE);
sprintf(dev->VolHdr.ProgDate, "Build %s %s", __DATE__, __TIME__);
break;
case EOS_LABEL:
if (dev->state & ST_TAPE) {
- jcr->EndBlock = dev->block_num;
- jcr->EndFile = dev->file;
+ jcr->EndBlock = dev->EndBlock;
+ jcr->EndFile = dev->EndFile;
} else {
jcr->EndBlock = (uint32_t)dev->file_addr;
jcr->EndFile = (uint32_t)(dev->file_addr >> 32);
/*
* First erase all memory of the current volume
*/
- dev->block_num = 0;
- dev->file = 0;
- dev->LastBlockNumWritten = 0;
+ dev->block_num = dev->file = 0;
+ dev->EndBlock = dev->EndFile = 0;
memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
memset(&jcr->VolCatInfo, 0, sizeof(jcr->VolCatInfo));
memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
/* */
#define VERSION "1.28"
#define VSTRING "1"
-#define DATE "12 December 2002"
-#define LSMDATE "12Dec02"
+#define DATE "16 December 2002"
+#define LSMDATE "16Dec02"
/* Debug flags */
#define DEBUG 1