FAT data corruption in 2.2.3

Tom Holroyd (tomh@nibh.go.jp)
Thu, 11 Mar 1999 16:44:10 +0900 (JST)


I'm on an AlphaPC164LX, egcs-1.1.1 compiled, stock 2.2.3.

I can reliably demonstrate data corruption when writing to a FAT floppy.
I never see data corruption when using an ext2 floppy, nor do I see
corruption on raw writes. Only mounted vfat or msdos floppies.

mformat a:
mount -t vfat /dev/fd0 /mnt/floppy

Then, cp -a some directory onto it, and verify the data. Do this in a
loop. For example,

#! /bin/sh

DEST=/mnt/floppy

cd $DEST
while true; do
cp -a /tmp/ppc $DEST/ppc
if ! diff -r /tmp/ppc $DEST/ppc; then
break
fi
rm -rf $DEST/ppc
done

After a few iterations I _always_ get a failure (when update runs). Now
the interesting part. The corruption is very specific, and I have traced
its source. I first made note of when the data (that will eventually get
trashed) reaches the kernel, at the copy_from_user() call in
fat_file_write() in fs/fat/file.c. I followed it to fat_brelse(), where I
first noticed that it became corrupted. Generating an oops, I get:

[fat_brelse+88/160]
[fat_write_inode+428/480]
[sync_inodes+220/352]
[sync_old_buffers+72/608]
[sys_bdflush+84/256]
[entSys+168/192]

What this means is that the data in this buffer head got interpreted as a
directory, and when the sync happened it scribbled inode junk on the data.
I was able to verify that the data in the bh gets destroyed inside
fat_write_inode(), where the b_data pointer is cast to an msdos_dir_entry
pointer. What's happening is that fat_write_inode() first does a
fat_bread(), and it's getting my data block. [I notice that the block
number is calculated from the inode number.]

I don't know whose fault this is, ultimately. Either the inode list is
getting trashed, so it thinks my data block is a dir_entry, or the buffer
my data was copied into was selected poorly (though I don't know how it
would know whether a block is destined to be an inode or not).

Note that it is almost always the same files that get trashed, in exactly
the same blocks. In one case, the bread() happens on inode 34512, so
block 2157, and that block is already owned by my data. Interestingly
when I look later there is no inode 34512 on the disk, but I may be
missing something.

I also tested Alexander Viro's rename-9 patch just for fun; it made no
difference.

I don't know if it's important or not, but the loop above really flies --
it seems to operate out of cached buffers mostly. When I use an ext2
floppy it goes much more slowly, keeping in synch with the disk. Maybe it
runs out? I've got 128M here...

I can provide an image of the disk I'm using, but I normally use arch/ppc
('cause it fits).

Dr. Tom Holroyd
I would dance and be merry,
Life would be a ding-a-derry,
If I only had a brain.
-- The Scarecrow

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/