[kpartx] Add suport for S/390 ECKD DASD
[platform/upstream/multipath-tools.git] / kpartx / dasd.c
1 /*
2  * dasd.c
3  *
4  * IBM DASD partition table handling.
5  *
6  * Mostly taken from drivers/s390/block/dasd.c
7  *
8  * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
23  * USA.
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <inttypes.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 #include <linux/hdreg.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <libdevmapper.h>
38 #include "devmapper.h"
39 #include "kpartx.h"
40 #include "byteorder.h"
41 #include "dasd.h"
42
43 /*
44  */
45 int 
46 read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
47 {
48         int retval = -1;
49         int blocksize, offset, size;
50         long disksize;
51         dasd_information_t info;
52         struct hd_geometry geo;
53         char type[5] = {0,};
54         char name[7] = {0,};
55         unsigned char *label_raw;
56         volume_label_t vlabel;
57         unsigned char *data = NULL;
58         unsigned int blk;
59         int fd_dasd = -1;
60         struct stat sbuf;
61         dev_t dev;
62         char *devname;
63         char pathname[256];
64
65         if (fd < 0) {
66                 return -1;
67         }
68
69         if (fstat(fd, &sbuf) == -1) {
70                 return -1;
71         }
72
73         devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev));
74
75         if (devname != NULL) {
76                 /* We were passed a handle to a dm device.
77                  * Get the first target and operate on that instead.
78                  */
79                 if (!(dev = dm_get_first_dep(devname))) {
80                         free(devname);
81                         return -1;
82                 }
83                 free(devname);
84
85                 if ((unsigned int)major(dev) != 94) {
86                         /* Not a DASD */
87                         return -1;
88                 }
89
90                 /*
91                  * Hard to believe, but there's no simple way to translate
92                  * major/minor into an openable device file, so we have
93                  * to create one for ourselves.
94                  */
95                 
96                 sprintf(pathname, "/dev/.kpartx-node-%u-%u",
97                         (unsigned int)major(dev), (unsigned int)minor(dev));
98                 if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
99                         /* Devicenode does not exist. Try to create one */
100                         if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
101                                 /* Couldn't create a device node */
102                                 return -1;
103                         }
104                         fd_dasd = open(pathname, O_RDONLY);
105                         /*
106                          * The file will vanish when the last process (we)
107                          * has ceased to access it.
108                          */
109                         unlink(pathname);
110                 }
111                 if (!fd_dasd) {
112                         /* Couldn't open the device */
113                         return -1;
114                 }
115         } else {
116                 fd_dasd = fd;
117         }
118
119         if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
120                 goto out;
121         }
122
123         if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
124                 goto out;
125         }
126         
127         if (ioctl(fd_dasd, BLKGETSIZE, &disksize) != 0)
128                 goto out;
129
130         if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
131                 goto out;
132
133         if (blocksize < 512 || blocksize > 4096)
134                 goto out;
135
136         /*
137          * Get volume label, extract name and type.
138          */
139
140         if (!(data = (unsigned char *)malloc(blocksize)))
141                 goto out;
142
143
144         if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1)
145                 goto out;
146         if (read(fd_dasd, data, blocksize) == -1) {
147                 perror("read");
148                 goto out;
149         }
150         vtoc_ebcdic_dec(data, type, 4);
151
152         if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
153                 label_raw = &data[8];
154         else
155                 label_raw = &data[4];
156
157         name[6] = '\0';
158         vtoc_ebcdic_dec(label_raw, name, 6);
159
160         memcpy (&vlabel, data, sizeof(volume_label_t));
161
162         /*
163          * Three different types: CMS1, VOL1 and LNX1/unlabeled
164          */
165         if (strncmp(type, "CMS1", 4) == 0) {
166                 /*
167                  * VM style CMS1 labeled disk
168                  */
169                 int *label = (int *) &vlabel;
170
171                 if (label[13] != 0) {
172                         /* disk is reserved minidisk */
173                         blocksize = label[3];
174                         offset = label[13];
175                         size = (label[7] - 1)*(blocksize >> 9);
176                 } else {
177                         offset = (info.label_block + 1) * (blocksize >> 9);
178                         size = disksize - offset;
179                 }
180                 sp[0].start = offset * (blocksize >> 9);
181                 sp[0].size = size - offset * (blocksize >> 9);
182                 retval = 1;
183         } else if ((strncmp(type, "VOL1", 4) == 0) &&
184                 (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) {
185                 /*
186                  * New style VOL1 labeled disk
187                  */
188                 int counter;
189
190                 /* get block number and read then go through format1 labels */
191                 blk = cchhb2blk(&vlabel.vtoc, &geo) + 1;
192                 counter = 0;
193                 if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1)
194                         goto out;
195
196                 while (read(fd_dasd, data, blocksize) != -1) {
197                         format1_label_t f1;
198
199                         memcpy(&f1, data, sizeof(format1_label_t));
200
201                         /* skip FMT4 / FMT5 / FMT7 labels */
202                         if (EBCtoASC[f1.DS1FMTID] == '4'
203                             || EBCtoASC[f1.DS1FMTID] == '5'
204                             || EBCtoASC[f1.DS1FMTID] == '7') {
205                                 blk++;
206                                 continue;
207                         }
208
209                         /* only FMT1 valid at this point */
210                         if (EBCtoASC[f1.DS1FMTID] != '1')
211                                 break;
212
213                         /* OK, we got valid partition data */
214                         offset = cchh2blk(&f1.DS1EXT1.llimit, &geo);
215                         size  = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - 
216                                 offset + geo.sectors;
217                         sp[counter].start = offset * (blocksize >> 9);
218                         sp[counter].size = size * (blocksize >> 9);
219                         counter++;
220                         blk++;
221                 }
222                 retval = counter;
223         } else {
224                 /*
225                  * Old style LNX1 or unlabeled disk
226                  */
227                 offset = (info.label_block + 1) * (blocksize >> 9);
228                 size = disksize - offset;
229                 sp[0].start = offset * (blocksize >> 9);
230                 sp[0].size = size - offset * (blocksize >> 9);
231                 retval = 1;
232         }
233
234  out:
235         if (data != NULL)
236                 free(data);
237         if (fd_dasd != -1 && fd_dasd != fd)
238                 close(fd_dasd);
239         return retval;
240 }