Kern's ToDo List
- 30 October 2002
+ 1 November 2002
To do:
- Document that two Verifys at same time on same client do not work.
- Document buffer size considerations with Sparse files --
- Document saving MySQL databases, where to find code for shutting
down and saving other databases.
+- Test restore options. Test bscan and MD5 in FileSet.
For 1.27 release:
- Continue improving the restore process (handling
position the tape, ...)
- Work more on how to to a Bacula restore beginning with
just a Bacula tape and a boot floppy (bare metal recovery).
+- Try bare metal Windows restore
-- Finish implementation of restore "replace" options, and document.
-- Write bcopy program.
-- Recovery of a bad tape (bcopy)
+- Write bcopy program -- recovery of bad tape.
+- Fix read_record to handle multiple sessions.
- Program files (i.e. execute a program to read/write files).
Pass read date of last backup, size of file last time.
- Add code to reject whole blocks if not wanted on restore.
- Possibly add email to Watchdog if drive is unmounted too
long and a job is waiting on the drive.
-- What to do with btime and JobTDate?
-- Add FileSet MD5 to bscan.
- Strip trailing slashes from Include directory names in the FD.
- Use read_record.c in SD code.
-- Try bare metal Windows restore
- Add EOM records ???????
- Why don't we get an error message from Win32 FD when bootstrap
file cannot be created for restore command?
create_volume_list) -- also in restore command?
- File system type from File daemon
- Move block size code from block.c to init_dev().
-
+- Add FileSet MD5 to bscan.
+- Finish implementation of restore "replace" options, and document.
#define S_ISWIN32 020000
#endif
+/* Replace codes needed in both file routines and non-file routines */
+/* Job replace codes -- in "replace" */
+#define REPLACE_ALWAYS 'a'
+#define REPLACE_IFNEWER 'w'
+#define REPLACE_NEVER 'n'
+#define REPLACE_IFOLDER 'o'
+
+
#endif /* _BACONFIG_H */
{"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},
+ {"replace", store_replace, ITEM(res_job.replace), REPLACE_ALWAYS, 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= */
struct s_kw ReplaceOptions[] = {
- {"always", 'a'}, /* always */
- {"ifnewer", 'w'},
- {"never", 'n'},
+ {"always", REPLACE_ALWAYS},
+ {"ifnewer", REPLACE_IFNEWER},
+ {"ifolder", REPLACE_IFOLDER},
+ {"never", REPLACE_NEVER},
{NULL, 0}
};
* Scan for Include options (keyword=option) is converted into one or
* two characters. Verifyopts=xxxx is Vxxxx:
*/
-static char *scan_include_options(LEX *lc, int keyword)
+static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
{
int token, i;
- static char opts[100];
char option[3];
option[0] = 0; /* default option = none */
token = lex_get_token(lc, T_NAME); /* expect at least one option */
if (keyword == INC_KW_VERIFY) { /* special case */
/* ***FIXME**** ensure these are in permitted set */
- strcpy(option, "V"); /* indicate Verify */
- strcat(option, lc->str);
- strcat(option, ":"); /* terminate it */
+ bstrncat(opts, "V", optlen); /* indicate Verify */
+ bstrncat(opts, lc->str, optlen);
+ bstrncat(opts, ":", optlen); /* terminate it */
} else {
for (i=0; FS_options[i].name; i++) {
if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
+ /* NOTE! maximum 2 letters here or increase option[3] */
option[0] = FS_options[i].option[0];
option[1] = FS_options[i].option[1];
i = 0;
if (i != 0) {
scan_err1(lc, "Expected a FileSet option keyword, got: %s", lc->str);
}
+ bstrncat(opts, option, optlen);
}
- strcat(opts, option);
/* check if more options are specified */
if (lc->ch != ',') {
}
token = lex_get_token(lc, T_ALL); /* yes, eat comma */
}
-
- return opts;
}
if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
scan_err1(lc, "expected an = following keyword, got: %s", lc->str);
}
- strcat(inc_opts, scan_include_options(lc, keyword));
+ scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
if (token == T_BOB) {
break;
}
if (jcr->job->replace != 0) {
replace = jcr->job->replace;
} else {
- replace = 'a'; /* always replace */
+ replace = REPLACE_ALWAYS; /* always replace */
}
if (jcr->RestoreWhere) {
where = jcr->RestoreWhere; /* override */
} else if (job->replace) {
jcr->replace = job->replace;
} else {
- jcr->replace = 'a';
+ jcr->replace = REPLACE_ALWAYS;
}
replace = ReplaceOptions[0].name;
for (i=0; ReplaceOptions[i].name; i++) {
print_ls_output(jcr, ofile, lname, type, &statp);
extract = create_file(jcr, fname, ofile, lname, type,
- stream, &statp, attribsEx, &ofd);
+ stream, &statp, attribsEx, &ofd, jcr->replace);
Dmsg1(40, "Extract=%d\n", extract);
if (extract) {
jcr->JobFiles++;
*/
int create_file(void *jcr, char *fname, char *ofile, char *lname,
int type, int stream, struct stat *statp,
- char *attribsEx, int *ofd)
+ char *attribsEx, int *ofd, int replace)
{
int new_mode, parent_mode, mode;
uid_t uid;
char *f, *p, savechr;
*ofd = -1;
-/*
- * new_mode = S_IRWXUGO & ~umask(0);
- */
new_mode = statp->st_mode;
Dmsg2(300, "newmode=%x file=%s\n", new_mode, ofile);
parent_mode = S_IWUSR | S_IXUSR | new_mode;
case FT_LNKSAVED: /* Hard linked, file already saved */
Dmsg2(130, "Hard link %s => %s\n", ofile, lname);
if (link(lname, ofile) != 0) {
- Jmsg3(jcr, M_ERROR, 0, "Could not hard link %s ==> %s: ERR=%s\n",
+ Jmsg3(jcr, M_ERROR, 0, _("Could not hard link %s ==> %s: ERR=%s\n"),
ofile, lname, strerror(errno));
}
break;
case FT_REGE: /* empty file */
case FT_REG: /* regular file */
+ /* If not always replacing, do a stat and decide */
+ if (replace != REPLACE_ALWAYS) {
+ struct stat mstatp;
+ if (lstat(ofile, &mstatp) == 0) {
+ switch (replace) {
+ case REPLACE_IFNEWER:
+ if (statp->st_mtime < mstatp.st_mtime) {
+ Jmsg1(jcr, M_INFO, 0, _("File %s skipped. Not newer.\n"), ofile);
+ return 0;
+ }
+ break;
+ case REPLACE_IFOLDER:
+ if (statp->st_mtime > mstatp.st_mtime) {
+ Jmsg1(jcr, M_INFO, 0, _("File %s skipped. Not older.\n"), ofile);
+ return 0;
+ }
+ break;
+ case REPLACE_NEVER:
+ Jmsg1(jcr, M_INFO, 0, _("File %s skipped. Already exists.\n"), ofile);
+ return 0;
+ }
+ }
+ }
/* Separate pathname and filename */
for (p=f=ofile; *p; p++) {
if (*p == '/') {
fnl = p - f;
if (fnl == 0) {
- Jmsg1(jcr, M_ERROR, 0, "Zero length filename: %s\n", fname);
+ Jmsg1(jcr, M_ERROR, 0, _("Zero length filename: %s\n"), fname);
return 0;
}
pnl = f - ofile - 1;
if (pnl <= 0) {
- Jmsg1(jcr, M_ERROR, 0, "Zero length path: %s\n", fname);
+ Jmsg1(jcr, M_ERROR, 0, _("Zero length path: %s\n"), fname);
return 0;
}
savechr = ofile[pnl];
}
Dmsg1(50, "Create file: %s\n", ofile);
if ((*ofd = open(ofile, mode, S_IRUSR | S_IWUSR)) < 0) {
- Jmsg2(jcr, M_ERROR, 0, "Could not create %s: ERR=%s\n", ofile, strerror(errno));
+ Jmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"), ofile, strerror(errno));
return 0;
}
return 1;
case FT_LNK:
Dmsg2(130, "FT_LNK should restore: %s -> %s\n", ofile, lname);
if (symlink(lname, ofile) != 0 && errno != EEXIST) {
- Jmsg3(jcr, M_ERROR, 0, "Could not symlink %s -> %s: ERR=%s\n",
+ Jmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"),
ofile, lname, strerror(errno));
}
return 0;
case FT_DIR:
Dmsg2(300, "Make dir mode=%o dir=%s\n", new_mode, ofile);
if (make_path(jcr, ofile, new_mode, parent_mode, uid, gid, 0, NULL) != 0) {
- Jmsg1(jcr, M_ERROR, 0, "Could not make directory: %s\n", ofile);
+ Jmsg1(jcr, M_ERROR, 0, _("Could not make directory: %s\n"), ofile);
}
return 0;
case FT_SPEC:
if (S_ISFIFO(statp->st_mode)) {
Dmsg1(0, "Restore fifo: %s\n", ofile);
if (mkfifo(ofile, statp->st_mode) != 0) {
- Jmsg2(jcr, M_ERROR, 0, "Cannot make fifo %s: ERR=%s\n", ofile, strerror(errno));
+ Jmsg2(jcr, M_ERROR, 0, _("Cannot make fifo %s: ERR=%s\n"), ofile, strerror(errno));
return 0;
}
} else {
Dmsg1(0, "Restore node: %s\n", ofile);
if (mknod(ofile, statp->st_mode, statp->st_rdev) != 0) {
- Jmsg2(jcr, M_ERROR, 0, "Cannot make node %s: ERR=%s\n", ofile, strerror(errno));
+ Jmsg2(jcr, M_ERROR, 0, _("Cannot make node %s: ERR=%s\n"), ofile, strerror(errno));
return 0;
}
}
case FT_NORECURSE:
case FT_NOFSCHG:
case FT_NOOPEN:
- Jmsg2(jcr, M_ERROR, 0, "Original file %s not saved. Stat=%d\n", fname, type);
+ Jmsg2(jcr, M_ERROR, 0, _("Original file %s not saved. Stat=%d\n"), fname, type);
return 0;
default:
- Jmsg2(jcr, M_ERROR, 0, "Unknown file type %d; not restored: %s\n", type, fname);
+ Jmsg2(jcr, M_ERROR, 0, _("Unknown file type %d; not restored: %s\n"), type, fname);
return 0;
}
#include "save-cwd.h"
-
/*
* NOTE!!! These go on the tape, so don't change them. If
* need be, add to them.
/* from create_file.c */
int create_file(void *jcr, char *fname, char *ofile, char *lname,
int type, int stream, struct stat *statp,
- char *attribsEx, int *ofd);
+ char *attribsEx, int *ofd, int replace);
/* From find.c */
FF_PKT *init_find_files();
/* Pmsg1(000, "Restoring: %s\n", ofile); */
extract = create_file(jcr, fname, ofile, lname, type, stream,
- &statp, attribsEx, &ofd);
+ &statp, attribsEx, &ofd, REPLACE_ALWAYS);
num_files++;
if (extract) {
/* Create FileSet record */
strcpy(fsr.FileSet, label.FileSetName);
+ strcpy(fsr.MD5, label.FileSetMD5);
create_fileset_record(db, &fsr);
jr.FileSetId = fsr.FileSetId;
return 1;
}
fsr->FileSetId = 0;
- fsr->MD5[0] = ' '; /* ***FIXME*** */
- fsr->MD5[1] = 0;
+ if (fsr->MD5[0] == 0) {
+ fsr->MD5[0] = ' '; /* Equivalent to nothing */
+ fsr->MD5[1] = 0;
+ }
if (db_get_fileset_record(db, fsr)) {
if (verbose) {
Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
}
if (label->VerNum >= 11) {
unser_string(label->FileSetMD5);
+ } else {
+ label->FileSetMD5[0] = 0;
}
if (rec->FileIndex == EOS_LABEL) {
unser_uint32(label->JobFiles);
/* */
#define VERSION "1.27"
#define VSTRING "1"
-#define DATE "30 October 2002"
-#define LSMDATE "30Oct02"
+#define DATE "01 November 2002"
+#define LSMDATE "01Nov02"
/* Debug flags */
#define DEBUG 1