Hello everyone,
I have updated the patch that adds -root and -old-root
to mkisofs. It is now based on cdrtools 2.01a16, but this
is not the major reason for the update, as the patch applied
to that version fine before. Instead it fixes a problem with
files in subdirectories from previous backups being found in
the latest one even if the corresponding file had been deleted.
I also prepared a script that uses the new options and growisofs
to do full and incremental backups. A full verify is possible
with GNU tar --diff or star -diff - please read the script for
details...
Andy, can you please update the copy of this patch on your
growisofs page?
--
Bye, Patrick Ohly
--
[EMAIL PROTECTED]
[EMAIL PROTECTED] (MakeCD related mails)
http://home.pages.de/~ohly/
http://makecd.core.de/ (MakeCD home page)
This patch adds two new command line options:
-root dir
Moves all files and directories into dir in the
image. This is essentially the same as using
-graft-points and adding dir in front of every
pathspec, but is easier to use.
dir may actually be several levels deep. It is cre�
ated with the same permissions as other graft
points.
-old-root dir
This option is necessary when writing a multises�
sion image and the previous (or even older) session
was written with -root dir. Using a directory name
not found in the previous session causes mkisofs to
abort with an error.
Without this option, mkisofs would not be able to
find unmodified files and would be forced to write
their data into the image once more.
-root and -old-root are meant to be used together
to do incremental backups. The initial session
would e.g. use: mkisofs -root backup_1 <dirs>. The
next incremental backup with mkisofs -root backup_2
-old-root backup_1 <dirs> would take another snap�
shot of these directories. The first snapshot would
be found in backup_1, the second one in backup_2,
but only modified or new files need to be written
into the second session.
It is applied by unpacking cdrtools-2.01a16.tar.gz, entering
the cdrtools-2.1/mkisofs directory and typing
patch -p1 <mkisofs-2.01a16-root.diff
Then compile normally.
The patch should also work cdrtools-2.0, you just need
to resolve one conflict in a function declaration. However,
mkisofs 2.0 may create corrupt ISO images and thus should
no longer be used.
This revision of the patch also fixes a problem with deleted
files in subdirectories were kept visible after an incremental
backup with -old-root.
diff -r -c /tmp/cdrtools-2.01/mkisofs/mkisofs.8 cdrtools-2.01-patched/mkisofs/mkisofs.8
*** /tmp/cdrtools-2.01/mkisofs/mkisofs.8 Sat Jun 21 13:11:05 2003
--- cdrtools-2.01-patched/mkisofs/mkisofs.8 Sun Jul 20 09:18:53 2003
***************
*** 927,932 ****
--- 927,981 ----
written to the CD image - which may be useful in order to optimize the
data layout on a CD. See README.sort for more details.
.TP
+ .BI \-root " dir
+ Moves all files and directories into
+ .I dir
+ in the image. This is essentially the
+ same as using
+ .B -graft-points
+ and adding
+ .I dir
+ in front of every pathspec, but is easier to use.
+
+ .I dir
+ may actually be several levels deep. It is
+ created with the same permissions as other graft points.
+ .TP
+ .BI \-old-root " dir
+ This option is necessary when writing a multisession
+ image and the previous (or even older) session was written with
+ .BI -root " dir.
+ Using a directory name not found in the previous session
+ causes
+ .B mkisofs
+ to abort with an error.
+
+ Without this option,
+ .B mkisofs
+ would not be able to find unmodified files and would
+ be forced to write their data into the image once more.
+
+ .B \-root
+ and
+ .B \-old-root
+ are meant to be used together to do incremental backups.
+ The initial session would e.g. use:
+ mkisofs \-root backup_1 <dirs>.
+ The next incremental backup with mkisofs \-root backup_2
+ \-old-root backup_1 <dirs>
+ would take another snapshot of these directories. The first
+ snapshot would be found in backup_1, the second one in
+ backup_2, but only modified or new files need to be written
+ into the second session.
+
+ Without these options, new files would be added and old ones would be
+ preserved. But old ones would be overwritten if the file was
+ modified. Recovering the files by copying the whole directory back
+ from CD would also restore files that were deleted
+ intentionally. Accessing several older versions of a file requires
+ support by the operating system to choose which sessions are to be
+ mounted.
+ .TP
.B \-split-output
Split the output image into several files of approximately 1 GB.
This helps to create DVD sized iso9660 images on operating systems without
diff -r -c /tmp/cdrtools-2.01/mkisofs/mkisofs.c cdrtools-2.01-patched/mkisofs/mkisofs.c
*** /tmp/cdrtools-2.01/mkisofs/mkisofs.c Sat Jun 21 21:17:14 2003
--- cdrtools-2.01-patched/mkisofs/mkisofs.c Sun Jul 20 09:18:53 2003
***************
*** 412,417 ****
--- 412,420 ----
#define OPTION_HFS_BLESS 2040
#define OPTION_HFS_PARMS 2041
+ #define OPTION_RELOC_ROOT 2042
+ #define OPTION_RELOC_OLD_ROOT 2043
+
#endif /* APPLE_HYB */
static int save_pname = 0;
***************
*** 521,526 ****
--- 524,533 ----
0, NULL, "Do not pad output to a multiple of 32k", ONE_DASH},
{{"prev-session", required_argument, NULL, 'M'},
'M', "FILE", "Set path to previous session to merge", ONE_DASH},
+ {{"root", required_argument, NULL, OPTION_RELOC_ROOT},
+ '\0', "DIR", "Set root directory for all new files and directories", ONE_DASH},
+ {{"old-root", required_argument, NULL, OPTION_RELOC_OLD_ROOT},
+ '\0', "DIR", "Set root directory in previous session that is searched for files", ONE_DASH},
{{"omit-version-number", no_argument, NULL, 'N'},
'N', NULL, "Omit version number from ISO9660 filename (violates ISO9660)", ONE_DASH},
{{"new-dir-mode", required_argument, NULL, OPTION_NEW_DIR_MODE},
***************
*** 1131,1136 ****
--- 1138,1145 ----
#endif
struct stat statbuf;
char *merge_image = NULL;
+ char *reloc_root = NULL;
+ char *reloc_old_root = NULL;
struct iso_directory_record *mrootp = NULL;
struct output_fragment *opnt;
int longind;
***************
*** 1492,1497 ****
--- 1501,1512 ----
case 'M':
merge_image = optarg;
break;
+ case OPTION_RELOC_ROOT:
+ reloc_root = optarg;
+ break;
+ case OPTION_RELOC_OLD_ROOT:
+ reloc_old_root = optarg;
+ break;
case 'N':
omit_version_number++;
warn_violate++;
***************
*** 2617,2622 ****
--- 2632,2643 ----
memset(&de, 0, sizeof (de));
+ /*
+ * PO:
+ * Isn't root NULL at this time anyway?
+ * I think it is created by the first call to
+ * find_or_create_directory() below.
+ */
de.filedir = root; /* We need this to bootstrap */
if (cdrecord_data != NULL && merge_image == NULL) {
***************
*** 2734,2746 ****
short_name = NULL;
! if (node != NULL) {
char *pnt;
char *xpnt;
! *node = '\0';
! escstrcpy(graft_point, arg);
! *node = '=';
/*
* Remove unwanted "./" & "/" sequences from start...
--- 2755,2782 ----
short_name = NULL;
! if (node != NULL || reloc_root) {
char *pnt;
char *xpnt;
+ size_t len;
+
+ /* insert -root prefix */
+ if (reloc_root != NULL) {
+ strcpy(graft_point, reloc_root);
+ len = strlen(graft_point);
+ if (graft_point[len] != '/' ) {
+ graft_point[len] = '/';
+ len++;
+ }
+ } else {
+ len = 0;
+ }
! if (node) {
! *node = '\0';
! escstrcpy(graft_point + len, arg);
! *node = '=';
! }
/*
* Remove unwanted "./" & "/" sequences from start...
***************
*** 2755,2761 ****
strcpy(graft_point, xpnt);
} while (xpnt > graft_point);
! node = escstrcpy(nodename, ++node);
graft_dir = root;
xpnt = graft_point;
--- 2791,2801 ----
strcpy(graft_point, xpnt);
} while (xpnt > graft_point);
! if (node) {
! node = escstrcpy(nodename, ++node);
! } else {
! node = arg;
! }
graft_dir = root;
xpnt = graft_point;
***************
*** 2915,2921 ****
* side, since we may need to create some additional directories.
*/
if (merge_image != NULL) {
! if (merge_previous_session(root, mrootp) < 0) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD, "Cannot merge previous session.\n");
#else
--- 2955,2962 ----
* side, since we may need to create some additional directories.
*/
if (merge_image != NULL) {
! if (merge_previous_session(root, mrootp,
! reloc_root, reloc_old_root) < 0) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD, "Cannot merge previous session.\n");
#else
diff -r -c /tmp/cdrtools-2.01/mkisofs/mkisofs.h cdrtools-2.01-patched/mkisofs/mkisofs.h
*** /tmp/cdrtools-2.01/mkisofs/mkisofs.h Wed Apr 30 00:15:59 2003
--- cdrtools-2.01-patched/mkisofs/mkisofs.h Sun Jul 20 09:20:56 2003
***************
*** 493,499 ****
struct directory_entry * dpnt));
extern int rr_flags __PR((struct iso_directory_record *idr));
extern int merge_previous_session __PR((struct directory *,
! struct iso_directory_record *));
extern int get_session_start __PR((int *));
/* joliet.c */
--- 493,499 ----
struct directory_entry * dpnt));
extern int rr_flags __PR((struct iso_directory_record *idr));
extern int merge_previous_session __PR((struct directory *,
! struct iso_directory_record *, char *, char *));
extern int get_session_start __PR((int *));
/* joliet.c */
diff -r -c /tmp/cdrtools-2.01/mkisofs/multi.c cdrtools-2.01-patched/mkisofs/multi.c
*** /tmp/cdrtools-2.01/mkisofs/multi.c Wed Apr 30 00:18:10 2003
--- cdrtools-2.01-patched/mkisofs/multi.c Sun Jul 20 16:01:13 2003
***************
*** 1530,1538 ****
* directory entries, so that we can determine how large each directory is.
*/
int
! merge_previous_session(this_dir, mrootp)
struct directory *this_dir;
struct iso_directory_record *mrootp;
{
struct directory_entry **orig_contents = NULL;
struct directory_entry *odpnt = NULL;
--- 1530,1540 ----
* directory entries, so that we can determine how large each directory is.
*/
int
! merge_previous_session(this_dir, mrootp, reloc_root, reloc_old_root)
struct directory *this_dir;
struct iso_directory_record *mrootp;
+ char *reloc_root;
+ char *reloc_old_root;
{
struct directory_entry **orig_contents = NULL;
struct directory_entry *odpnt = NULL;
***************
*** 1544,1558 ****
--- 1546,1677 ----
lstatbuf;
int retcode;
+ /* skip leading slash */
+ while (reloc_old_root && reloc_old_root[0] == PATH_SEPARATOR) {
+ reloc_old_root++;
+ }
+ while (reloc_root && reloc_root[0] == PATH_SEPARATOR) {
+ reloc_root++;
+ }
+
/*
* Parse the same directory in the image that we are merging for
* multisession stuff.
*/
orig_contents = read_merging_directory(mrootp, &n_orig);
if (orig_contents == NULL) {
+ if (reloc_old_root) {
+ #ifdef USE_LIBSCHILY
+ comerrno(EX_BAD, "Reading old session failed, cannot execute -old-root.\n");
+ #else
+ fprintf(stderr, "Reading old session failed, cannot execute -old-root.\n");
+ #endif
+ return (-1);
+ }
return (0);
}
+ if (reloc_old_root && reloc_old_root[0]) {
+ struct directory_entry **new_orig_contents = orig_contents;
+ int new_n_orig = n_orig;
+
+ /* decend until we reach the original root */
+ while (reloc_old_root[0]) {
+ int i;
+ char *next;
+ int last;
+
+ for (next = reloc_old_root; *next && *next != PATH_SEPARATOR; next++);
+ if (*next) {
+ last = 0;
+ *next = 0;
+ next++;
+ } else {
+ last = 1;
+ }
+ while (*next == PATH_SEPARATOR) {
+ next++;
+ }
+
+ for (i = 0; i < new_n_orig; i++) {
+ struct iso_directory_record subroot;
+
+ if (new_orig_contents[i]->name != NULL
+ && strcmp(new_orig_contents[i]->name, reloc_old_root) != 0) {
+ /* Not the same name continue */
+ continue;
+ }
+ /*
+ * enter directory, free old one only if not the top level,
+ * which is still needed
+ */
+ subroot = new_orig_contents[i]->isorec;
+ if (new_orig_contents != orig_contents) {
+ free_mdinfo(new_orig_contents, new_n_orig);
+ }
+ new_orig_contents = read_merging_directory(&subroot, &new_n_orig);
+
+ if (!new_orig_contents) {
+ #ifdef USE_LIBSCHILY
+ comerrno(EX_BAD, "Reading directory %s in old session failed, cannot execute -old-root.\n", reloc_old_root );
+ #else
+ fprintf(stderr, "Reading directory %s in old session failed, cannot execute -old-root.\n", reloc_old_root );
+ #endif
+ return (-1);
+ }
+
+ i = -1;
+ break;
+ }
+
+ if (i == new_n_orig) {
+ #ifdef USE_LIBSCHILY
+ comerrno(EX_BAD, "-old-root (sub)directory %s not found in old session.\n", reloc_old_root );
+ #else
+ fprintf(stderr, "-old-root (sub)directory %s not found in old session.\n", reloc_old_root );
+ #endif
+ return (-1);
+ }
+
+ /* restore string, proceed to next sub directory */
+ if (!last) {
+ reloc_old_root[strlen(reloc_old_root)] = PATH_SEPARATOR;
+ }
+ reloc_old_root = next;
+ }
+
+ /*
+ * preserve the old session, skipping those dirs/files that are found again
+ * in the new root
+ */
+ for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) {
+ status = stat_filter(s_entry->whole_name, &statbuf);
+ lstatus = lstat_filter(s_entry->whole_name, &lstatbuf);
+
+ /* check_prev_session() will search for s_entry and remove it from
+ orig_contents if found */
+ retcode = check_prev_session(orig_contents, n_orig, s_entry,
+ &statbuf, &lstatbuf, NULL);
+ if (retcode == -1)
+ return (-1);
+ }
+ merge_remaining_entries(this_dir, orig_contents, n_orig);
+
+ /* use new directory */
+ free_mdinfo(orig_contents, n_orig);
+ orig_contents = new_orig_contents;
+ n_orig = new_n_orig;
+
+ if (reloc_root && reloc_root[0]) {
+ /* also decend into new root before searching for files */
+ this_dir = find_or_create_directory(this_dir, reloc_root, NULL, TRUE);
+ if (!this_dir) {
+ return (-1);
+ }
+ }
+ }
+
+
/*
* Now we scan the directory itself, and look at what is inside of it.
*/
***************
*** 1613,1619 ****
s_entry->whole_name,
s_entry, 1);
dflag = merge_previous_session(child,
! &odpnt->isorec);
if (dflag == -1) {
return (-1);
}
--- 1732,1739 ----
s_entry->whole_name,
s_entry, 1);
dflag = merge_previous_session(child,
! &odpnt->isorec,
! NULL, reloc_old_root );
if (dflag == -1) {
return (-1);
}
***************
*** 1623,1633 ****
}
}
! /*
! * Whatever is left over, are things which are no longer in the tree on
! * disk. We need to also merge these into the tree.
! */
! merge_remaining_entries(this_dir, orig_contents, n_orig);
free_mdinfo(orig_contents, n_orig);
return (1);
}
--- 1743,1755 ----
}
}
! if (!reloc_old_root) {
! /*
! * Whatever is left over, are things which are no longer in the tree on
! * disk. We need to also merge these into the tree.
! */
! merge_remaining_entries(this_dir, orig_contents, n_orig);
! }
free_mdinfo(orig_contents, n_orig);
return (1);
}
#! /bin/sh
#
# An working example for a script that does
# either a full or incremental backup and
# can verify the backup. It requires growisofs
# and either star or a tar that supports d/--diff
# and -X (GNU tar is okay).
#
# Author: Patrick Ohly, [EMAIL PROTECTED]
# License: do whatever you like with it...
#
set -e
#
# Edit this script to customize it:
#
# directories to be backed up - without leading
# slash, but relative to /
dirs="etc var/spool usr/local/bin usr/local/home"
# the DVD writer
dev=/dev/scd1
# intermediate mount point for backup
mnt=/mnt
# intermediate file with exclude patterns (not deleted,
# to allow inspection)
exclude=/tmp/backup.exclude
# a logfile of backup/verify process
log=/tmp/backup.log
# the image will have one subdirectory with this
# prefix followed by...
prefix=backup-
# ... the current date/time in this format
current="$prefix`date +%Y-%m-%d-%H:%M`"
# file exclusion patterns, see mkisofs -exclude-list
cat >$exclude <<EOF
*/.netscape/cache
*/.galeon/mozilla/galeon/Cache
var/spool/squid
var/spool/cruft
*~
EOF
# other mkisofs options - note that -J is not used
# here because it caused mkisofs to abort due to
# filename collisions. It might be useful to read
# the backup under Windows. -D is used to preserve
# the directory structure, even if that requires
# breaking the ISO 9660 standard.
mkisofsopts="-R -D"
case "$1" in
incremental)
mode=-M
mount $dev $mnt
root="-root $current -old-root `cd $mnt && ls -d $prefix* | tail -1`"
umount $mnt
;;
full)
mode=-Z
root="-root $current"
;;
verify)
mount $dev $mnt
# choose the tar command that better suits you...
cmd="(cd $mnt/`cd $mnt && ls -d $prefix* | tail -1` && tar cf - *) | (cd / && tar df - -X $exclude $dirs ) 2>&1 | tee $log"
# cmd="(cd $mnt/`cd $mnt && ls -d $prefix* | tail -1` && star -c -f - *) | (cd / && star -diff diffopts=perm,type,uid,gid,size,data,rdev,symlink,mtime,ctime -f - -V `sed -e 's/\(.*\)/pat=\1/' $exclude` $dirs ) 2>&1 | tee $log"
echo $cmd
set +e
eval $cmd
res=$?
umount $mnt
exit $res
;;
*)
echo "usage: $0 full|incremental|verify"
exit 1
;;
esac
cd /
cmd="growisofs $mode $dev $root -quiet $mkisofsopts -exclude-list $exclude -graft-points `for i in $dirs; do echo $i=$i; done` 2>&1 | tee $log"
echo $cmd
eval $cmd
exit $?