Merge branch 'master' of git://git.kernel.org/pub/scm/linux/storage/multipath-tools/
[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 unsigned long sectors512(unsigned long sectors, int blocksize)
44 {
45         return sectors * (blocksize >> 9);
46 }
47
48 /*
49  */
50 int 
51 read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
52 {
53         int retval = -1;
54         int blocksize;
55         long disksize;
56         unsigned long offset, size;
57         dasd_information_t info;
58         struct hd_geometry geo;
59         char type[5] = {0,};
60         char name[7] = {0,};
61         unsigned char *label_raw;
62         volume_label_t vlabel;
63         unsigned char *data = NULL;
64         unsigned int blk;
65         int fd_dasd = -1;
66         struct stat sbuf;
67         dev_t dev;
68         char *devname;
69         char pathname[256];
70
71         if (fd < 0) {
72                 return -1;
73         }
74
75         if (fstat(fd, &sbuf) == -1) {
76                 return -1;
77         }
78
79         devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev));
80
81         if (devname != NULL) {
82                 /* We were passed a handle to a dm device.
83                  * Get the first target and operate on that instead.
84                  */
85                 if (!(dev = dm_get_first_dep(devname))) {
86                         free(devname);
87                         return -1;
88                 }
89                 free(devname);
90
91                 if ((unsigned int)major(dev) != 94) {
92                         /* Not a DASD */
93                         return -1;
94                 }
95
96                 /*
97                  * Hard to believe, but there's no simple way to translate
98                  * major/minor into an openable device file, so we have
99                  * to create one for ourselves.
100                  */
101                 
102                 sprintf(pathname, "/dev/.kpartx-node-%u-%u",
103                         (unsigned int)major(dev), (unsigned int)minor(dev));
104                 if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
105                         /* Devicenode does not exist. Try to create one */
106                         if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
107                                 /* Couldn't create a device node */
108                                 return -1;
109                         }
110                         fd_dasd = open(pathname, O_RDONLY);
111                         /*
112                          * The file will vanish when the last process (we)
113                          * has ceased to access it.
114                          */
115                         unlink(pathname);
116                 }
117                 if (!fd_dasd) {
118                         /* Couldn't open the device */
119                         return -1;
120                 }
121         } else {
122                 fd_dasd = fd;
123         }
124
125         if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
126                 goto out;
127         }
128
129         if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
130                 goto out;
131         }
132         
133         if (ioctl(fd_dasd, BLKGETSIZE, &disksize) != 0)
134                 goto out;
135
136         if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
137                 goto out;
138
139         if (blocksize < 512 || blocksize > 4096)
140                 goto out;
141
142         /*
143          * Get volume label, extract name and type.
144          */
145
146         if (!(data = (unsigned char *)malloc(blocksize)))
147                 goto out;
148
149
150         if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1)
151                 goto out;
152         if (read(fd_dasd, data, blocksize) == -1) {
153                 perror("read");
154                 goto out;
155         }
156         vtoc_ebcdic_dec(data, type, 4);
157
158         if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
159                 label_raw = &data[8];
160         else
161                 label_raw = &data[4];
162
163         name[6] = '\0';
164         vtoc_ebcdic_dec(label_raw, name, 6);
165
166         memcpy (&vlabel, data, sizeof(volume_label_t));
167
168         /*
169          * Three different types: CMS1, VOL1 and LNX1/unlabeled
170          */
171         if (strncmp(type, "CMS1", 4) == 0) {
172                 /*
173                  * VM style CMS1 labeled disk
174                  */
175                 int *label = (int *) &vlabel;
176
177                 if (label[13] != 0) {
178                         /* disk is reserved minidisk */
179                         blocksize = label[3];
180                         offset = label[13];
181                         size   = sectors512(label[7] - 1, blocksize);
182                 } else {
183                         offset = info.label_block + 1;
184                         size   = disksize;
185                 }
186                 sp[0].start = sectors512(offset, blocksize);
187                 sp[0].size  = size - sp[0].start;
188                 retval = 1;
189         } else if ((strncmp(type, "VOL1", 4) == 0) &&
190                 (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) {
191                 /*
192                  * New style VOL1 labeled disk
193                  */
194                 int counter;
195
196                 /* get block number and read then go through format1 labels */
197                 blk = cchhb2blk(&vlabel.vtoc, &geo) + 1;
198                 counter = 0;
199                 if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1)
200                         goto out;
201
202                 while (read(fd_dasd, data, blocksize) != -1) {
203                         format1_label_t f1;
204
205                         memcpy(&f1, data, sizeof(format1_label_t));
206
207                         /* skip FMT4 / FMT5 / FMT7 labels */
208                         if (EBCtoASC[f1.DS1FMTID] == '4'
209                             || EBCtoASC[f1.DS1FMTID] == '5'
210                             || EBCtoASC[f1.DS1FMTID] == '7') {
211                                 blk++;
212                                 continue;
213                         }
214
215                         /* only FMT1 valid at this point */
216                         if (EBCtoASC[f1.DS1FMTID] != '1')
217                                 break;
218
219                         /* OK, we got valid partition data */
220                         offset = cchh2blk(&f1.DS1EXT1.llimit, &geo);
221                         size  = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - 
222                                 offset + geo.sectors;
223                         sp[counter].start = sectors512(offset, blocksize);
224                         sp[counter].size  = sectors512(size, blocksize);
225                         counter++;
226                         blk++;
227                 }
228                 retval = counter;
229         } else {
230                 /*
231                  * Old style LNX1 or unlabeled disk
232                  */
233                 sp[0].start = sectors512(info.label_block + 1, blocksize);
234                 sp[0].size  = disksize - sp[0].start;
235                 retval = 1;
236         }
237
238  out:
239         if (data != NULL)
240                 free(data);
241         if (fd_dasd != -1 && fd_dasd != fd)
242                 close(fd_dasd);
243         return retval;
244 }