#!/bin/bash
###########################################################################
#
# Shell program to compare and verify cd-rom media.
#
# Copyright 1998-2002, William Shotts, Jr.
# <bshotts@users.sourceforge.net>
#
# 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.
#
# This software is part of the LinuxCommand.org project, a site for
# Linux education and advocacy devoted to helping users of legacy
# operating systems migrate into the future.
#
# You may contact the LinuxCommand.org project at:
#
# http://www.linuxcommand.org
#
# Description:
#
# This program is used to compare 2 cd-roms. It can compare based on a
# comparison of the entire cd (every block of data), by comparing the
# name, size and modification date of each file, or by calculating
# and comparing the checksum of each file. Note that this program
# compares cd-roms, not audio cds.
#
# For the file comparison methods, the results are presented in three
# lists, first a list of files that were found on both the master
# and the copy cds, then a list of files found only on the master and
# a list of those files found only on the copy. If two cds are
# identical, all the files will appear on the first and no files will
# appear on the second and third lists.
#
# Note the constants CDROM and CDMOUNT which may need to be changed
# to support your particular system.
#
# Usage:
#
# compare_cds [ -h | --help ] | [ -r | -d | -c ]
#
# Options:
#
# -h, --help Display this help message and exit.
# -r (Raw) caclulate checksum for entire cd-rom
# -d (Directory) compare file name, dates and size.
# -c (Checksum) Compare checksums for each file.
#
#
# Revisions:
#
# 04/17/1998 File created
# 02/01/1999 Changed compare_file_lists to save results to
# ./compare_cds.txt - WS
# 02/02/2000 Program modernized and extended to support file
# names with embedded spaces - WS
# 03/01/2000 Minor cosmetic changes - WS
# 04/03/2000 Made viewing of file listing optional - WS
# 04/24/2001 Improved mount detection method - WS
# 02/17/2002 Cosmetic updates, improvements to whole disk
# comparisons, temp file security fixes - WS
#
# $Id: compare_cds,v 1.3 2002/02/18 14:18:38 bshotts Exp $
#
###########################################################################
###########################################################################
# Constants
###########################################################################
PROGNAME=$(basename $0)
CDROM=/dev/cdrom # CD device - Adjust this for your system
CDMOUNT=/mnt/cdrom # Mount point - Adjust this for your system
if [ -d ~/tmp ]; then
TEMPDIR=~/tmp
else
TEMPDIR=/tmp
fi
MASTER_SUMFILE=${TEMPDIR}/${PROGNAME}.master.$$.$RANDOM
COPY_SUMFILE=${TEMPDIR}/${PROGNAME}.copy.$$.$RANDOM
MASTER=1
COPY=2
###########################################################################
# Functions
###########################################################################
function is_cd_mounted
{
#####
# Determines if cd is mounted
# Arguments:
# none
#####
local mnt_pt
# Look at the third field in the output of mount and see if it
# exactly equals the cd-rom mount point
mnt_pt=$(mount | awk -v cdmount=$CDMOUNT '$3 == cdmount { print $3 } ')
if [ "$mnt_pt" != "" ]; then
return 0
else
return 1
fi
} # end of is_cd_mounted
function clean_up
{
#####
# Function to remove temporary files and other housekeeping
# No arguments
#####
rm -f ${MASTER_SUMFILE}
rm -f ${COPY_SUMFILE}
if is_cd_mounted; then
umount $CDMOUNT
fi
}
function graceful_exit
{
#####
# Function called for a graceful exit
# No arguments
#####
clean_up
exit
}
function error_exit
{
#####
# Function for exit due to fatal program error
# Accepts 1 argument
# string containing descriptive error message
#####
local err_msg
err_msg="${PROGNAME}: ${1}"
echo ${err_msg} >&2
clean_up
exit 1
}
function term_exit
{
#####
# Function to perform exit if termination signal is trapped
# No arguments
#####
echo "${PROGNAME}: Terminated"
clean_up
exit
}
function int_exit
{
#####
# Function to perform exit if interrupt signal is trapped
# No arguments
#####
echo "${PROGNAME}: Aborted by user"
clean_up
exit
}
function usage
{
#####
# Function to display usage message (does not exit)
# No arguments
#####
echo "Usage: ${PROGNAME} [ -h | --help ] | [ -r | -d | -c ]"
}
function helptext
{
#####
# Function to display help message for program
# No arguments
#####
local tab=$(echo -en "\t\t")
cat <<- -EOF-
This is a program to compare and verify cd-rom media.
$(usage)
Options:
-h, --help Display this help message and exit.
-r (Raw) caclulate checksum for entire cd-rom
-d (Directory) compare file name, dates and size.
-c (Checksum) Compare checksums for each file.
-EOF-
}
function mount_cd
{
#####
# Mounts the cd-rom drive
# Arguments:
# none
#####
if ! is_cd_mounted ; then
mount ${CDMOUNT} || error_exit "Cannot mount ${CDMOUNT}"
fi
} # end of mount_cd
function unmount_cd
{
#####
# Unmounts the cd-rom drive if it is mounted
# Arguments:
# none
#####
if is_cd_mounted ; then
cd
umount ${CDMOUNT} || error_exit "Cannot unmount ${CDMOUNT}"
fi
} # end of unmount_cd
function write_file_list
{
#####
# Writes a file containing a list of the files on a cd
# Arguments:
# 1 flag indicating if cd is master or copy (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "write_file_list: missing argument 1"
fi
case $1 in
${MASTER} ) checksum_list=${MASTER_SUMFILE}
disk_name="MASTER"
;;
${COPY} ) checksum_list=${COPY_SUMFILE}
disk_name="COPY";;
esac
echo "Reading ${disk_name} CD and constructing list of files..."
mount_cd
current_dir=$(pwd)
cd ${CDMOUNT}
find . -type f -printf "%-40p%-26t%s\n" | sort > ${checksum_list}
cd ${current_dir}
unmount_cd
} # end of write_file_list
function write_checksum_list
{
#####
# Writes a file containing a list of files and their checksums
# Arguments:
# 1 flag indicating if cd is a master or a copy (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "write_checksum_list: missing argument 1"
fi
case $1 in
${MASTER} ) checksum_list=${MASTER_SUMFILE}
disk_name="MASTER"
;;
${COPY} ) checksum_list=${COPY_SUMFILE}
disk_name="COPY"
;;
esac
echo "Reading ${disk_name} CD and constructing list of file checksums..."
mount_cd
current_dir=$(pwd)
cd ${CDMOUNT}
find . -type f | awk '
{
printf("%-60s %-5s", toupper($0), " ")
system("sum " "\"" $0 "\"")
}
' | sort > ${checksum_list}
cd ${current_dir}
unmount_cd
} # end of write_checksum_list
function read_cd_checksum
{
#####
# Calculates checksum for entire cd
# Arguments:
# 1 Flag indicating if cd is master or a copy (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "read_cd_checksum: missing argument 1"
fi
case $1 in
${MASTER} ) echo "Reading MASTER CD and calculating checksum..."
mount_cd
master_block_count=$(get_block_count)
unmount_cd
master_checksum=$(checksum_cd ${master_block_count})
;;
${COPY} ) echo "Reading COPY CD and calculating checksum..."
mount_cd
copy_block_count=$(get_block_count)
unmount_cd
if [ "${master_block_count}" = "${copy_block_count}" ]
then
copy_checksum=$(checksum_cd ${copy_block_count})
fi
;;
esac
} # end of read_cd_checksum
function compare_file_lists
{
#####
# Compares two files and describes differences
# Arguments:
# none
#####
local foo
{
echo -e "The following files were found on BOTH CDs:\n"
comm -12 ${MASTER_SUMFILE} ${COPY_SUMFILE}
echo -e "\n\nThe following files were found *only* on the MASTER CD:\n"
comm -23 ${MASTER_SUMFILE} ${COPY_SUMFILE}
echo -e "\n\nThe following files were found *only* on the COPY CD:\n"
comm -13 ${MASTER_SUMFILE} ${COPY_SUMFILE}
} > compare_cds.txt
if [ "$(comm -23 ${MASTER_SUMFILE} ${COPY_SUMFILE})" = "" -a "$(comm -13 ${MASTER_SUMFILE} ${COPY_SUMFILE})" = "" ]; then
echo -e "\n\aFile lists are identical."
else
echo -e "\n\aFile lists are *NOT* identical!"
fi
echo -en "View details [y/n]? > "
read foo
if [ "$foo" = "y" ]; then
less compare_cds.txt
fi
} # end of compare_file_lists
function compare_cd_checksums
{
#####
# Compares the checksums of two cds
# Arguments:
# none
#####
if [ "${master_block_count}" = "${copy_block_count}" ]
then
echo -e "Checksums:\n\tMASTER = ${master_checksum}\n\tCOPY = ${copy_checksum}"
if [ "${master_checksum}" = "${copy_checksum}" ]
then
echo "CDs are identical!"
else
echo "CDs are different!"
fi
else
echo -e "Block Counts:\n\tMASTER = ${master_block_count}\n\tCOPY = ${copy_block_count}"
echo "CDs have different block counts!"
echo "CDs are different!"
fi
} # end of compare_cd_checksums
function get_block_count
{
#####
# Determines the number of data blocks on a cd
# Arguments:
# none
#####
df -k | awk -v cd_mount=$CDMOUNT ' $6 == cd_mount { print $2 }'
} # end of get_block_count
function checksum_cd
{
#####
# Calculate the checksum of a cd
# Arguments:
# Number of data blocks on cd (Required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "checksum_cd: missing argument 1"
fi
dd if=${CDROM} count=$1 bs=1024 | md5sum | awk '{ print $1 }'
} # end of checksum_cd
###########################################################################
# Program starts here
###########################################################################
# Trap TERM, HUP, and INT signals and properly exit
trap term_exit TERM HUP
trap int_exit INT
# Process command line arguments
if [ $# -ne "1" ]; then
helptext
exit 1
fi
if [ "$1" = "--help" ]; then
helptext
graceful_exit
fi
# Process arguments
while getopts ":hrdc" opt; do
case $opt in
r ) mode_string="Performing byte comparison...\n"
read_routine="read_cd_checksum"
compare_routine="compare_cd_checksums"
;;
d ) mode_string="Performing directory comparison...\n"
read_routine="write_file_list"
compare_routine="compare_file_lists"
;;
c ) mode_string="Performing file checksum comparison...\n"
read_routine="write_checksum_list"
compare_routine="compare_file_lists"
;;
h ) helptext
graceful_exit
;;
* ) usage
exit 1
esac
done
echo -e ${mode_string}
echo -en "\n\aInsert MASTER CD [Enter or Ctrl-C to Quit]"
read reply
sleep 5 # give drive tile to "settle"
${read_routine} ${MASTER}
echo -en "\n\aInsert COPY CD [Enter or Ctrl-C to Quit]"
read reply
sleep 5 # give drive tile to "settle"
${read_routine} ${COPY}
${compare_routine}
graceful_exit
syntax highlighted by Code2HTML, v. 0.9.1