Imported Upstream version 0.5.0
[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  * Copyright IBM Corporation, 2009
10  *
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.
15  *
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.
20  *
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,
24  * USA.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <inttypes.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <linux/hdreg.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <libdevmapper.h>
39 #include "devmapper.h"
40 #include "kpartx.h"
41 #include "byteorder.h"
42 #include "dasd.h"
43
44 unsigned long long sectors512(unsigned long long sectors, int blocksize)
45 {
46         return sectors * (blocksize >> 9);
47 }
48
49 typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
50
51 /*
52  */
53 int 
54 read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
55 {
56         int retval = -1;
57         int blocksize;
58         uint64_t disksize;
59         uint64_t offset, size, fmt_size;
60         dasd_information_t info;
61         struct hd_geometry geo;
62         char type[5] = {0,};
63         volume_label_t vlabel;
64         unsigned char *data = NULL;
65         uint64_t blk;
66         int fd_dasd = -1;
67         struct stat sbuf;
68         dev_t dev;
69         char *devname;
70         char pathname[256];
71
72         if (fd < 0) {
73                 return -1;
74         }
75
76         if (fstat(fd, &sbuf) == -1) {
77                 return -1;
78         }
79
80         devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev));
81
82         if (devname != NULL) {
83                 /* We were passed a handle to a dm device.
84                  * Get the first target and operate on that instead.
85                  */
86                 if (!(dev = dm_get_first_dep(devname))) {
87                         free(devname);
88                         return -1;
89                 }
90                 free(devname);
91
92                 if ((unsigned int)major(dev) != 94) {
93                         /* Not a DASD */
94                         return -1;
95                 }
96
97                 /*
98                  * Hard to believe, but there's no simple way to translate
99                  * major/minor into an openable device file, so we have
100                  * to create one for ourselves.
101                  */
102                 
103                 sprintf(pathname, "/dev/.kpartx-node-%u-%u",
104                         (unsigned int)major(dev), (unsigned int)minor(dev));
105                 if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
106                         /* Devicenode does not exist. Try to create one */
107                         if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
108                                 /* Couldn't create a device node */
109                                 return -1;
110                         }
111                         fd_dasd = open(pathname, O_RDONLY);
112                         /*
113                          * The file will vanish when the last process (we)
114                          * has ceased to access it.
115                          */
116                         unlink(pathname);
117                 }
118                 if (!fd_dasd) {
119                         /* Couldn't open the device */
120                         return -1;
121                 }
122         } else {
123                 fd_dasd = fd;
124         }
125
126         if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
127                 goto out;
128         }
129
130         if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
131                 goto out;
132         }
133
134         if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0)
135                 goto out;
136         disksize >>= 9;
137
138         if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
139                 goto out;
140
141         if (blocksize < 512 || blocksize > 4096)
142                 goto out;
143
144         /*
145          * Get volume label, extract name and type.
146          */
147
148         if (!(data = (unsigned char *)malloc(blocksize)))
149                 goto out;
150
151
152         if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1)
153                 goto out;
154         if (read(fd_dasd, data, blocksize) == -1) {
155                 perror("read");
156                 goto out;
157         }
158
159         if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
160                 memcpy (&vlabel, data, sizeof(vlabel));
161         else {
162                 bzero(&vlabel,4);
163                 memcpy (&vlabel.vollbl, data, sizeof(vlabel) - 4);
164         }
165         vtoc_ebcdic_dec(vlabel.vollbl, type, 4);
166
167         /*
168          * Three different types: CMS1, VOL1 and LNX1/unlabeled
169          */
170         if (strncmp(type, "CMS1", 4) == 0) {
171                 /*
172                  * VM style CMS1 labeled disk
173                  */
174                 label_ints_t *label = (label_ints_t *) &vlabel;
175
176                 blocksize = label[4];
177                 if (label[14] != 0) {
178                         /* disk is reserved minidisk */
179                         offset = label[14];
180                         size   = sectors512(label[8] - 1, blocksize);
181                 } else {
182                         offset = info.label_block + 1;
183                         size   = sectors512(label[8], blocksize);
184                 }
185                 sp[0].start = sectors512(offset, blocksize);
186                 sp[0].size  = size - sp[0].start;
187                 retval = 1;
188         } else if ((strncmp(type, "VOL1", 4) == 0) &&
189                 (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) {
190                 /*
191                  * New style VOL1 labeled disk
192                  */
193                 int counter;
194
195                 /* get block number and read then go through format1 labels */
196                 blk = cchhb2blk(&vlabel.vtoc, &geo) + 1;
197                 counter = 0;
198                 if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1)
199                         goto out;
200
201                 while (read(fd_dasd, data, blocksize) != -1) {
202                         format1_label_t f1;
203
204                         memcpy(&f1, data, sizeof(format1_label_t));
205
206                         /* skip FMT4 / FMT5 / FMT7 labels */
207                         if (EBCtoASC[f1.DS1FMTID] == '4'
208                             || EBCtoASC[f1.DS1FMTID] == '5'
209                             || EBCtoASC[f1.DS1FMTID] == '7'
210                             || EBCtoASC[f1.DS1FMTID] == '9') {
211                                 blk++;
212                                 continue;
213                         }
214
215                         /* only FMT1 and FMT8 valid at this point */
216                         if (EBCtoASC[f1.DS1FMTID] != '1' &&
217                             EBCtoASC[f1.DS1FMTID] != '8')
218                                 break;
219
220                         /* OK, we got valid partition data */
221                         offset = cchh2blk(&f1.DS1EXT1.llimit, &geo);
222                         size  = cchh2blk(&f1.DS1EXT1.ulimit, &geo) -
223                                 offset + geo.sectors;
224                         sp[counter].start = sectors512(offset, blocksize);
225                         sp[counter].size  = sectors512(size, blocksize);
226                         counter++;
227                         blk++;
228                 }
229                 retval = counter;
230         } else {
231                 /*
232                  * Old style LNX1 or unlabeled disk
233                  */
234                 if (strncmp(type, "LNX1", 4) == 0) {
235                         if (vlabel.ldl_version == 0xf2) {
236                                 fmt_size = sectors512(vlabel.formatted_blocks,
237                                                       blocksize);
238                         } else if (!strcmp(info.type, "ECKD")) {
239                                 /* formated w/o large volume support */
240                                 fmt_size = geo.cylinders * geo.heads
241                                         * geo.sectors * (blocksize >> 9);
242                         } else {
243                                 /* old label and no usable disk geometry
244                                  * (e.g. DIAG) */
245                                 fmt_size = disksize;
246                         }
247                         size = disksize;
248                         if (fmt_size < size)
249                                 size = fmt_size;
250                 } else
251                         size = disksize;
252
253                 sp[0].start = sectors512(info.label_block + 1, blocksize);
254                 sp[0].size  = size - sp[0].start;
255                 retval = 1;
256         }
257
258  out:
259         if (data != NULL)
260                 free(data);
261         if (fd_dasd != -1 && fd_dasd != fd)
262                 close(fd_dasd);
263         return retval;
264 }