Bug in pdcraid.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi!

 

I’ve been making some (mostly cosmetic) changes to pdcraid.c and quite accidentally I came across a very interesting bug.

 

I’m in the middle of a lot of stuff so it won’t be easy to produce a patch for the next few days.  J

 

The bug will result in any drive which reports a native number of heads being anything other than 16 (technically any multiple of 8) will not mount.  Maybe this will help all those people who email the list saying ‘On xxx drive pdcraid mounts my array but with yyy drive it doesn’t’.

 

Here’s the bug:

 

static int read_disk_sb (int major, int minor, unsigned char *buffer,int bufsize)

{

            int ret = -EINVAL;

            struct buffer_head *bh = NULL;

            kdev_t dev = MKDEV(major,minor);

            unsigned long sb_offset;

 

            if (blksize_size[major]==NULL)   /* device doesn't exist */

                        return -EINVAL;

                      

           

            /*

             * Calculate the position of the superblock,

             * it's at first sector of the last cylinder

             */

            sb_offset = calc_pdcblock_offset(major,minor)/8;

            /* The /8 transforms sectors into 4Kb blocks */

 

 and then a little later…

 

static void __init probedisk(int devindex,int device, int raidlevel)

{

            int i;

            int major, minor;

        struct promise_raid_conf *prom;

            static unsigned char block[4096];

 

            if (devlist[devindex].device!=-1) /* already assigned to another array */

                        return;

           

            major = devlist[devindex].major;

            minor = devlist[devindex].minor;

 

        if (read_disk_sb(major,minor,(unsigned char*)&block,sizeof(block)))

            return;

                                                                                                                 

        prom = (struct promise_raid_conf*)&block[512];

 

 

First you calculate the lba address of the superblock and immediately divide it by 8 (for bread).  The problem is that in the second snippet of code you assume that the block you’re looking for is one sector into the buffer.  In fact it always is in that location if the native heads is 16.  However if the native heads is at 15, the buffer you’re looking for is the 4th sector in the buffer.  Oops, now the superblock won’t be found and you’ll end up not finding the array.

 

Here’s what I did to remedy the problem:

 

In read_disk_sb:

 

            /*

             * Calculate the position of the superblock,

             * it's at first sector of the last cylinder

             */

            sb_offset = calc_pdcblock_offset(major,minor);

 

            if (sb_offset==0)

                        return -1;          

           

            set_blocksize (dev, 4096);

 

            bh = bread (dev, sb_offset/8, 8192);

            /* The /8 transforms sectors into 4Kb blocks */

           

            if (bh) {

                        memcpy (buffer, bh->b_data + ((sb_offset%8)*512), bufsize);

            } else {

                        printk(KERN_ERR "pdcraid: Error reading superblock.\n");

                        goto abort;

            }

 

Notice that I’m reading 16 sectors instead of 8.  This is mostly a precaution.  I also adjust the memcpy so that the superblock always gets copied to the beginning of the buffer.

 

This means that I also changed.

 

        prom = (struct promise_raid_conf*)&block[512];

to:

        prom = (struct promise_raid_conf*)block;

 

 

And that’s it.  If you want a patch, I can generate one on Monday.

 

Also, as a side note, I’ve made some other changes (those cosmetic changes mentioned earlier).  I noticed that in the source, you never check the superblock for PR_MAGIC.  Is there any reason why this is not happening?  I would think this would make probedisk a little more secure against incorrectly mounting a wrong superblock.

 

And, the one that started my foray into this, I made a change so that probedisk would only mount the superblock if the drive was running on the pdc202xx chipset.  While it is neat that you can take a drive set that has been running on the promise controller and effectively remove the promise controller and everything still works, my problem was that the drive had long since been removed from the promise controller and the superblock described a configuration which was no longer valid.  Maybe this should be a kernel option or maybe a module option?

 

 

BEGIN:VCARD
VERSION:2.1
N:Burr;Phillip
FN:Phillip Burr
ORG:;Development
TITLE:Software Engineer
TEL;WORK;VOICE:+1-801-437-8317
EMAIL;PREF;INTERNET:Phillip.Burr@xxxxxxxxxxxxxx
REV:20020211T183314Z
END:VCARD

[Index of Archives]     [Linux RAID]     [Linux Device Mapper]     [Linux IDE]     [Linux SCSI]     [Kernel]     [Linux Books]     [Linux Admin]     [GFS]     [RPM]     [Yosemite Campgrounds]     [AMD 64]

  Powered by Linux