quarantine release might lose mail?

Frank Cusack fcusack at fcusack.com
Tue Dec 15 23:00:01 GMT 2009

On December 15, 2009 10:52:46 AM -0500 Frank Cusack <fcusack at fcusack.com> 
> Especially not, as in this case, where the fix is trivial and especially
> not when the problem and solution is known.  I will post my qrelease
> program later today.

Here it is.  One thing I learned is that the queue file name includes
a timestamp, so the chance of a collision is much, much less than I'd
originally guessed.  Very very close to zero, I would expect.  But
the fix is trivial and guarantees correctness.  Maybe this can make
it onto the wiki page.

# qrelease - release a mail from the mailscanner quarantine for postfix
# The mailscanner quarantine directory and the postfix queue directory must
# be on the same filesystem.
# See also

# warn needs to prefix with progname, not function name

  echo $0: $* >&2

  warn $*
  exit 1

if [ $# -ne 1 ]
  echo "Usage: $0 QUARANTINE" >&2
  exit 1

[ -d "$1" ] || die "Directory $1 does not exist"

pd=$(postconf -h queue_directory)/incoming
perl -e "exit 1 if (stat '$qd/.')[0] != (stat '$pd/.')[0]" ||
  die "$qd and $pd are not the same filesystem"

# die on any errors not explicitly caught
set -e

qf=${qd##*/}	# basename
qf=${qf%.*}	# prefix
[ -f $qd/$qf ] || die "Queue File $qd/$qf does not exist"

# The queue file name has a microseconds timestamp portion and an inum 
# The inum portion guarantees a unique name in the filesystem (required
# to prevent loss of data across the multiple queues).
# The time portion avoids recycling the queue id's quickly, so that an
# individual email can more easily be traced.
# Note that the queue filename in the quarantine is incorrect as it 
# the original queue filename, but is a COPY of the original queue file (ie,
# the inum portion is wrong).  Because of the timestamp portion, it is 
# unlikely to collide but it is possible.  The chance of collision depends 
# the resolution of the system clock and the rate of inode reuse.  We 
# it because having an ABSOLUTE guarantee is nice and also it's trivial.

# We preserve the timestamp portion.
ts=$(printf %.5s $qf)

# make a copy first
cp $qd/$qf $qd/$ts.rename
# rename the queue file to match inum
pf=$ts$(printf %X $(ls -i $qd/$ts.rename | awk '{print $1}'))
mv $qd/$ts.rename $qd/$pf

# move to postfix queue dir
mv -i $qd/$pf $pd/$pf </dev/null || die "Filename collision!"
chown postfix $pd/$pf
chmod 0700 $pd/$pf && logger -p mail.info -t qrelease "$qf requeued as $pf"
exit 0

More information about the MailScanner mailing list