4 * IBM DASD partition table handling.
6 * Mostly taken from drivers/s390/block/dasd.c
8 * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH
9 * Copyright IBM Corporation, 2009
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
31 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <linux/hdreg.h>
38 #include <libdevmapper.h>
39 #include "devmapper.h"
41 #include "byteorder.h"
44 unsigned long long sectors512(unsigned long long sectors, int blocksize)
46 return sectors * (blocksize >> 9);
50 * Magic records per track calculation, copied from fdasd.c
52 static unsigned int ceil_quot(unsigned int d1, unsigned int d2)
54 return (d1 + (d2 - 1)) / d2;
57 unsigned int recs_per_track(unsigned int dl)
59 int dn = ceil_quot(dl + 6, 232) + 1;
60 return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
64 typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
69 read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
74 uint64_t offset, size, fmt_size;
75 dasd_information_t info;
76 struct hd_geometry geo;
78 volume_label_t vlabel;
79 unsigned char *data = NULL;
91 if (fstat(fd, &sbuf) == -1) {
95 devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev));
97 if (devname != NULL) {
98 /* We were passed a handle to a dm device.
99 * Get the first target and operate on that instead.
101 if (!(dev = dm_get_first_dep(devname))) {
107 if ((unsigned int)major(dev) != 94) {
113 * Hard to believe, but there's no simple way to translate
114 * major/minor into an openable device file, so we have
115 * to create one for ourselves.
118 sprintf(pathname, "/dev/.kpartx-node-%u-%u",
119 (unsigned int)major(dev), (unsigned int)minor(dev));
120 if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
121 /* Devicenode does not exist. Try to create one */
122 if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
123 /* Couldn't create a device node */
126 fd_dasd = open(pathname, O_RDONLY);
128 * The file will vanish when the last process (we)
129 * has ceased to access it.
134 /* Couldn't open the device */
141 if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
142 info.label_block = 2;
144 memcpy(info.type, "ECKD", sizeof(info.type));
147 if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
150 if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0)
153 if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
157 geo.sectors = recs_per_track(blocksize);
158 cyl = disksize / (blocksize * geo.heads * geo.sectors);
159 if (cyl < LV_COMPAT_CYL)
162 geo.cylinders = LV_COMPAT_CYL;
168 if (blocksize < 512 || blocksize > 4096)
172 * Get volume label, extract name and type.
175 if (!(data = (unsigned char *)malloc(blocksize)))
179 if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1)
181 if (read(fd_dasd, data, blocksize) == -1) {
186 if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
187 memcpy (&vlabel, data, sizeof(vlabel));
190 memcpy (&vlabel.vollbl, data, sizeof(vlabel) - 4);
192 vtoc_ebcdic_dec(vlabel.vollbl, type, 4);
195 * Three different types: CMS1, VOL1 and LNX1/unlabeled
197 if (strncmp(type, "CMS1", 4) == 0) {
199 * VM style CMS1 labeled disk
201 label_ints_t *label = (label_ints_t *) &vlabel;
203 blocksize = label[4];
204 if (label[14] != 0) {
205 /* disk is reserved minidisk */
207 size = sectors512(label[8] - 1, blocksize);
209 offset = info.label_block + 1;
210 size = sectors512(label[8], blocksize);
212 sp[0].start = sectors512(offset, blocksize);
213 sp[0].size = size - sp[0].start;
215 } else if ((strncmp(type, "VOL1", 4) == 0) &&
216 (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) {
218 * New style VOL1 labeled disk
222 /* get block number and read then go through format1 labels */
223 blk = cchhb2blk(&vlabel.vtoc, &geo) + 1;
225 if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1)
228 while (read(fd_dasd, data, blocksize) != -1) {
231 memcpy(&f1, data, sizeof(format1_label_t));
233 /* skip FMT4 / FMT5 / FMT7 labels */
234 if (EBCtoASC[f1.DS1FMTID] == '4'
235 || EBCtoASC[f1.DS1FMTID] == '5'
236 || EBCtoASC[f1.DS1FMTID] == '7'
237 || EBCtoASC[f1.DS1FMTID] == '9') {
242 /* only FMT1 and FMT8 valid at this point */
243 if (EBCtoASC[f1.DS1FMTID] != '1' &&
244 EBCtoASC[f1.DS1FMTID] != '8')
247 /* OK, we got valid partition data */
248 offset = cchh2blk(&f1.DS1EXT1.llimit, &geo);
249 size = cchh2blk(&f1.DS1EXT1.ulimit, &geo) -
250 offset + geo.sectors;
251 sp[counter].start = sectors512(offset, blocksize);
252 sp[counter].size = sectors512(size, blocksize);
259 * Old style LNX1 or unlabeled disk
261 if (strncmp(type, "LNX1", 4) == 0) {
262 if (vlabel.ldl_version == 0xf2) {
263 fmt_size = sectors512(vlabel.formatted_blocks,
265 } else if (!strcmp(info.type, "ECKD")) {
266 /* formated w/o large volume support */
267 fmt_size = geo.cylinders * geo.heads
268 * geo.sectors * (blocksize >> 9);
270 /* old label and no usable disk geometry
280 sp[0].start = sectors512(info.label_block + 1, blocksize);
281 sp[0].size = size - sp[0].start;
288 if (fd_dasd != -1 && fd_dasd != fd)