linux/fs/partitions/ibm.c
Horst Hummel 90f0094dc6 [PATCH] s390: dasd partition detection
DASD allows to open a device as soon as gendisk is registered, which means the
device is a fake device (capacity=0) and we do know nothing about blocksize
and partitions at that point of time.  In case the device is opened by
someone, the bdev and inode creation is done with the fake device info and the
following partition detection code is just using the wrong data.

To avoid this modify the DASD state machine to make sure that the open is
rejected until the device analysis is either finished or an unformatted device
was detected.

Signed-off-by: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-08 14:14:01 -08:00

197 lines
4.8 KiB
C

/*
* File...........: linux/fs/partitions/ibm.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Volker Sameske <sameske@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* History of changes (starts July 2000)
* 07/10/00 Fixed detection of CMS formatted disks
* 02/13/00 VTOC partition support added
* 12/27/01 fixed PL030593 (CMS reserved minidisk not detected on 64 bit)
* 07/24/03 no longer using contents of freed page for CMS label recognition (BZ3611)
*/
#include <linux/config.h>
#include <linux/buffer_head.h>
#include <linux/hdreg.h>
#include <linux/slab.h>
#include <asm/dasd.h>
#include <asm/ebcdic.h>
#include <asm/uaccess.h>
#include <asm/vtoc.h>
#include "check.h"
#include "ibm.h"
/*
* compute the block number from a
* cyl-cyl-head-head structure
*/
static inline int
cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
return ptr->cc * geo->heads * geo->sectors +
ptr->hh * geo->sectors;
}
/*
* compute the block number from a
* cyl-cyl-head-head-block structure
*/
static inline int
cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
return ptr->cc * geo->heads * geo->sectors +
ptr->hh * geo->sectors +
ptr->b;
}
/*
*/
int
ibm_partition(struct parsed_partitions *state, struct block_device *bdev)
{
int blocksize, offset, size;
loff_t i_size;
dasd_information_t *info;
struct hd_geometry *geo;
char type[5] = {0,};
char name[7] = {0,};
union label_t {
struct vtoc_volume_label vol;
struct vtoc_cms_label cms;
} *label;
unsigned char *data;
Sector sect;
blocksize = bdev_hardsect_size(bdev);
if (blocksize <= 0)
return 0;
i_size = i_size_read(bdev->bd_inode);
if (i_size == 0)
return 0;
if ((info = kmalloc(sizeof(dasd_information_t), GFP_KERNEL)) == NULL)
goto out_noinfo;
if ((geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL)) == NULL)
goto out_nogeo;
if ((label = kmalloc(sizeof(union label_t), GFP_KERNEL)) == NULL)
goto out_nolab;
if (ioctl_by_bdev(bdev, BIODASDINFO, (unsigned long)info) != 0 ||
ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
goto out_noioctl;
/*
* Get volume label, extract name and type.
*/
data = read_dev_sector(bdev, info->label_block*(blocksize/512), &sect);
if (data == NULL)
goto out_readerr;
strncpy (type, data, 4);
if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD")))
strncpy(name, data + 8, 6);
else
strncpy(name, data + 4, 6);
memcpy(label, data, sizeof(union label_t));
put_dev_sector(sect);
EBCASC(type, 4);
EBCASC(name, 6);
/*
* Three different types: CMS1, VOL1 and LNX1/unlabeled
*/
if (strncmp(type, "CMS1", 4) == 0) {
/*
* VM style CMS1 labeled disk
*/
if (label->cms.disk_offset != 0) {
printk("CMS1/%8s(MDSK):", name);
/* disk is reserved minidisk */
blocksize = label->cms.block_size;
offset = label->cms.disk_offset;
size = (label->cms.block_count - 1) * (blocksize >> 9);
} else {
printk("CMS1/%8s:", name);
offset = (info->label_block + 1);
size = i_size >> 9;
}
put_partition(state, 1, offset*(blocksize >> 9),
size-offset*(blocksize >> 9));
} else if ((strncmp(type, "VOL1", 4) == 0) &&
(!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
/*
* New style VOL1 labeled disk
*/
unsigned int blk;
int counter;
printk("VOL1/%8s:", name);
/* get block number and read then go through format1 labels */
blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
counter = 0;
while ((data = read_dev_sector(bdev, blk*(blocksize/512),
&sect)) != NULL) {
struct vtoc_format1_label f1;
memcpy(&f1, data, sizeof(struct vtoc_format1_label));
put_dev_sector(sect);
/* skip FMT4 / FMT5 / FMT7 labels */
if (f1.DS1FMTID == _ascebc['4']
|| f1.DS1FMTID == _ascebc['5']
|| f1.DS1FMTID == _ascebc['7']) {
blk++;
continue;
}
/* only FMT1 valid at this point */
if (f1.DS1FMTID != _ascebc['1'])
break;
/* OK, we got valid partition data */
offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
offset + geo->sectors;
if (counter >= state->limit)
break;
put_partition(state, counter + 1,
offset * (blocksize >> 9),
size * (blocksize >> 9));
counter++;
blk++;
}
} else {
/*
* Old style LNX1 or unlabeled disk
*/
if (strncmp(type, "LNX1", 4) == 0)
printk ("LNX1/%8s:", name);
else
printk("(nonl)/%8s:", name);
offset = (info->label_block + 1);
size = i_size >> 9;
put_partition(state, 1, offset*(blocksize >> 9),
size-offset*(blocksize >> 9));
}
printk("\n");
kfree(label);
kfree(geo);
kfree(info);
return 1;
out_readerr:
out_noioctl:
kfree(label);
out_nolab:
kfree(geo);
out_nogeo:
kfree(info);
out_noinfo:
return 0;
}