Bug#280213: exim4: Thinks it's always out of spool space

John Goerzen John Goerzen <jgoerzen@complete.org>, 280213@bugs.debian.org
Mon, 8 Nov 2004 13:35:15 -0600


On Mon, Nov 08, 2004 at 09:25:56AM +0100, Andreas Metzler wrote:
> > So you can see there is an issue.
> [...]
> > Architecture: alpha
> > Kernel: Linux 2.6.4-rc2
> [...]

I tried this on an i386 machine and did not see a problem.  So I dove
into the source and found that receive.c consistently is casting stuff
in the statvfs() structure (yes, this code uses statvfs(), NOT
statfs()).

Now, on Linux, this structure is defined as:

         struct statvfs {
           unsigned long  f_bsize;    /* file system block size */
           unsigned long  f_frsize;   /* fragment size */
           fsblkcnt_t     f_blocks;   /* size of fs in f_frsize units */
           fsblkcnt_t     f_bfree;    /* # free blocks */
           fsblkcnt_t     f_bavail;   /* # free blocks for non-root */
           fsfilcnt_t     f_files;    /* # inodes */
           fsfilcnt_t     f_ffree;    /* # free inodes */
           fsfilcnt_t     f_favail;   /* # free inodes for non-root */
           unsigned long  f_fsid;     /* file system id */
           unsigned long  f_flag;     /* mount flags */
           unsigned long  f_namemax;  /* maximum filename length */
         };

fsblkcnt_t is a 32-bit signed value, and fsfilcnt_t is a 32-bit unsigned
value (unless large files are in use, in which case they are both 64-bit
values).  

Consider this:

Type      | Size i386 | Size Alpha
----------+-----------+-----------
int       | 32 bits   | 32 bits
long      | 32 bits   | 64 bits
long long | 64 bits   | 64 bits

Now, in received.c, there is code such as:

  if (statbuf.F_BAVAIL < (unsigned long)
        ((((double)check_spool_space) * 1024.0 + (double)msg_size) /
            (double)statbuf.F_FRSIZE)
       ||

This could would break without long files enabled (32-bit value, 64-bit
unsigned long test)?

    debug_printf("spool directory %s space = %d blocks; inodes = %d; "
      "check_space = %dK (%d blocks); inodes = %d; msg_size = %d (%d blocks)\n",
      spool_directory, (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL,
      check_spool_space,
      (int)(((double)check_spool_space * 1024.0) / (double)statbuf.F_FRSIZE),
      check_spool_inodes, msg_size, (int)(msg_size / statbuf.F_FRSIZE));

Here we are casing statbuf.F_BAVAIL to an int, which could break *with*
long files enabled (converting a 64-bit value to a 32-bit one.)  I
wonder if the problem is not here necessarily.

However, my theory may not be right, since no manner of changes here
made the statvfs() call actually print out the right stuff.  So I'm
confused.

> Please stop exim, and run
> strace -o /tmp/straceout -vf -s 132 exim4 -d -bd 2>&1 |\
>    tee /tmp/exim.debug

That never worked, but I connected manually and attached strace to the
new process with -p.  Get this:

> kill the exim running as root with <Ctrl>-C and send this info:
> 1) The output of 
>    grep -2 -E 'stat.*/var/spool/exim4' /tmp/straceout

erwin:~# grep -2 -E 'stat.*/var/spool/exim4' /tmp/traceout
getxpid()                               = 18039
write(2, "18039 SMTP<< MAIL FROM:<jgoerzen"..., 60) = 60
statfs("/var/spool/exim4", {f_type="REISERFS_SUPER_MAGIC",
f_fbsize=4096, f_blocks=293066, f_bfree=55410, f_bavail=55410,
f_files=0, f_ffree=0, f_fsid={0, 0}, f_namelen=255, f_frsize=4096}) = 0
getxpid()                               = 18039
write(2, "18039 spool directory /var/spool"..., 145) = 145

> 2) The corresponding "spool directory /var/spool/exim4 space..."-line
> in /tmp/exim.debug

18039 spool directory /var/spool/exim4 space = 0 blocks; inodes = 4096;
check_space = 0K (0 blocks); inodes = 0; msg_size = 1005000 (245 blocks)
18039   spool directory space check failed: space=0 inodes=4096

How's that for weird?

And again, in Python (which calls statvfs too), it all worked fine.

I'm extremely puzzled.

-- John