NFSv4: Ensure that we remove NFSv4.0 delegations when state has expired
[platform/adaptation/renesas_rcar/renesas_kernel.git] / block / partitions / ibm.c
1 /*
2  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
3  *                  Volker Sameske <sameske@de.ibm.com>
4  * Bugreports.to..: <Linux390@de.ibm.com>
5  * Copyright IBM Corp. 1999, 2012
6  */
7
8 #include <linux/buffer_head.h>
9 #include <linux/hdreg.h>
10 #include <linux/slab.h>
11 #include <asm/dasd.h>
12 #include <asm/ebcdic.h>
13 #include <asm/uaccess.h>
14 #include <asm/vtoc.h>
15
16 #include "check.h"
17 #include "ibm.h"
18
19
20 union label_t {
21         struct vtoc_volume_label_cdl vol;
22         struct vtoc_volume_label_ldl lnx;
23         struct vtoc_cms_label cms;
24 };
25
26 /*
27  * compute the block number from a
28  * cyl-cyl-head-head structure
29  */
30 static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
31 {
32         sector_t cyl;
33         __u16 head;
34
35         /* decode cylinder and heads for large volumes */
36         cyl = ptr->hh & 0xFFF0;
37         cyl <<= 12;
38         cyl |= ptr->cc;
39         head = ptr->hh & 0x000F;
40         return cyl * geo->heads * geo->sectors +
41                head * geo->sectors;
42 }
43
44 /*
45  * compute the block number from a
46  * cyl-cyl-head-head-block structure
47  */
48 static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
49 {
50         sector_t cyl;
51         __u16 head;
52
53         /* decode cylinder and heads for large volumes */
54         cyl = ptr->hh & 0xFFF0;
55         cyl <<= 12;
56         cyl |= ptr->cc;
57         head = ptr->hh & 0x000F;
58         return  cyl * geo->heads * geo->sectors +
59                 head * geo->sectors +
60                 ptr->b;
61 }
62
63 static int find_label(struct parsed_partitions *state,
64                       dasd_information2_t *info,
65                       struct hd_geometry *geo,
66                       int blocksize,
67                       sector_t *labelsect,
68                       char name[],
69                       char type[],
70                       union label_t *label)
71 {
72         Sector sect;
73         unsigned char *data;
74         sector_t testsect[3];
75         unsigned char temp[5];
76         int found = 0;
77         int i, testcount;
78
79         /* There a three places where we may find a valid label:
80          * - on an ECKD disk it's block 2
81          * - on an FBA disk it's block 1
82          * - on an CMS formatted FBA disk it is sector 1, even if the block size
83          *   is larger than 512 bytes (possible if the DIAG discipline is used)
84          * If we have a valid info structure, then we know exactly which case we
85          * have, otherwise we just search through all possebilities.
86          */
87         if (info) {
88                 if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
89                     (info->cu_type == 0x3880 && info->dev_type == 0x3370))
90                         testsect[0] = info->label_block;
91                 else
92                         testsect[0] = info->label_block * (blocksize >> 9);
93                 testcount = 1;
94         } else {
95                 testsect[0] = 1;
96                 testsect[1] = (blocksize >> 9);
97                 testsect[2] = 2 * (blocksize >> 9);
98                 testcount = 3;
99         }
100         for (i = 0; i < testcount; ++i) {
101                 data = read_part_sector(state, testsect[i], &sect);
102                 if (data == NULL)
103                         continue;
104                 memcpy(label, data, sizeof(*label));
105                 memcpy(temp, data, 4);
106                 temp[4] = 0;
107                 EBCASC(temp, 4);
108                 put_dev_sector(sect);
109                 if (!strcmp(temp, "VOL1") ||
110                     !strcmp(temp, "LNX1") ||
111                     !strcmp(temp, "CMS1")) {
112                         if (!strcmp(temp, "VOL1")) {
113                                 strncpy(type, label->vol.vollbl, 4);
114                                 strncpy(name, label->vol.volid, 6);
115                         } else {
116                                 strncpy(type, label->lnx.vollbl, 4);
117                                 strncpy(name, label->lnx.volid, 6);
118                         }
119                         EBCASC(type, 4);
120                         EBCASC(name, 6);
121                         *labelsect = testsect[i];
122                         found = 1;
123                         break;
124                 }
125         }
126         if (!found)
127                 memset(label, 0, sizeof(*label));
128
129         return found;
130 }
131
132 static int find_vol1_partitions(struct parsed_partitions *state,
133                                 struct hd_geometry *geo,
134                                 int blocksize,
135                                 char name[],
136                                 union label_t *label)
137 {
138         sector_t blk;
139         int counter;
140         char tmp[64];
141         Sector sect;
142         unsigned char *data;
143         loff_t offset, size;
144         struct vtoc_format1_label f1;
145         int secperblk;
146
147         snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
148         strlcat(state->pp_buf, tmp, PAGE_SIZE);
149         /*
150          * get start of VTOC from the disk label and then search for format1
151          * and format8 labels
152          */
153         secperblk = blocksize >> 9;
154         blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
155         counter = 0;
156         data = read_part_sector(state, blk * secperblk, &sect);
157         while (data != NULL) {
158                 memcpy(&f1, data, sizeof(struct vtoc_format1_label));
159                 put_dev_sector(sect);
160                 /* skip FMT4 / FMT5 / FMT7 labels */
161                 if (f1.DS1FMTID == _ascebc['4']
162                     || f1.DS1FMTID == _ascebc['5']
163                     || f1.DS1FMTID == _ascebc['7']
164                     || f1.DS1FMTID == _ascebc['9']) {
165                         blk++;
166                         data = read_part_sector(state, blk * secperblk, &sect);
167                         continue;
168                 }
169                 /* only FMT1 and 8 labels valid at this point */
170                 if (f1.DS1FMTID != _ascebc['1'] &&
171                     f1.DS1FMTID != _ascebc['8'])
172                         break;
173                 /* OK, we got valid partition data */
174                 offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
175                 size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
176                         offset + geo->sectors;
177                 offset *= secperblk;
178                 size *= secperblk;
179                 if (counter >= state->limit)
180                         break;
181                 put_partition(state, counter + 1, offset, size);
182                 counter++;
183                 blk++;
184                 data = read_part_sector(state, blk * secperblk, &sect);
185         }
186         strlcat(state->pp_buf, "\n", PAGE_SIZE);
187
188         if (!data)
189                 return -1;
190
191         return 1;
192 }
193
194 static int find_lnx1_partitions(struct parsed_partitions *state,
195                                 struct hd_geometry *geo,
196                                 int blocksize,
197                                 char name[],
198                                 union label_t *label,
199                                 sector_t labelsect,
200                                 loff_t i_size,
201                                 dasd_information2_t *info)
202 {
203         loff_t offset, geo_size, size;
204         char tmp[64];
205         int secperblk;
206
207         snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
208         strlcat(state->pp_buf, tmp, PAGE_SIZE);
209         secperblk = blocksize >> 9;
210         if (label->lnx.ldl_version == 0xf2) {
211                 size = label->lnx.formatted_blocks * secperblk;
212         } else {
213                 /*
214                  * Formated w/o large volume support. If the sanity check
215                  * 'size based on geo == size based on i_size' is true, then
216                  * we can safely assume that we know the formatted size of
217                  * the disk, otherwise we need additional information
218                  * that we can only get from a real DASD device.
219                  */
220                 geo_size = geo->cylinders * geo->heads
221                         * geo->sectors * secperblk;
222                 size = i_size >> 9;
223                 if (size != geo_size) {
224                         if (!info) {
225                                 strlcat(state->pp_buf, "\n", PAGE_SIZE);
226                                 return 1;
227                         }
228                         if (!strcmp(info->type, "ECKD"))
229                                 if (geo_size < size)
230                                         size = geo_size;
231                         /* else keep size based on i_size */
232                 }
233         }
234         /* first and only partition starts in the first block after the label */
235         offset = labelsect + secperblk;
236         put_partition(state, 1, offset, size - offset);
237         strlcat(state->pp_buf, "\n", PAGE_SIZE);
238         return 1;
239 }
240
241 static int find_cms1_partitions(struct parsed_partitions *state,
242                                 struct hd_geometry *geo,
243                                 int blocksize,
244                                 char name[],
245                                 union label_t *label,
246                                 sector_t labelsect)
247 {
248         loff_t offset, size;
249         char tmp[64];
250         int secperblk;
251
252         /*
253          * VM style CMS1 labeled disk
254          */
255         blocksize = label->cms.block_size;
256         secperblk = blocksize >> 9;
257         if (label->cms.disk_offset != 0) {
258                 snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
259                 strlcat(state->pp_buf, tmp, PAGE_SIZE);
260                 /* disk is reserved minidisk */
261                 offset = label->cms.disk_offset * secperblk;
262                 size = (label->cms.block_count - 1) * secperblk;
263         } else {
264                 snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
265                 strlcat(state->pp_buf, tmp, PAGE_SIZE);
266                 /*
267                  * Special case for FBA devices:
268                  * If an FBA device is CMS formatted with blocksize > 512 byte
269                  * and the DIAG discipline is used, then the CMS label is found
270                  * in sector 1 instead of block 1. However, the partition is
271                  * still supposed to start in block 2.
272                  */
273                 if (labelsect == 1)
274                         offset = 2 * secperblk;
275                 else
276                         offset = labelsect + secperblk;
277                 size = label->cms.block_count * secperblk;
278         }
279
280         put_partition(state, 1, offset, size-offset);
281         strlcat(state->pp_buf, "\n", PAGE_SIZE);
282         return 1;
283 }
284
285
286 /*
287  * This is the main function, called by check.c
288  */
289 int ibm_partition(struct parsed_partitions *state)
290 {
291         struct block_device *bdev = state->bdev;
292         int blocksize, res;
293         loff_t i_size, offset, size;
294         dasd_information2_t *info;
295         struct hd_geometry *geo;
296         char type[5] = {0,};
297         char name[7] = {0,};
298         sector_t labelsect;
299         union label_t *label;
300
301         res = 0;
302         blocksize = bdev_logical_block_size(bdev);
303         if (blocksize <= 0)
304                 goto out_exit;
305         i_size = i_size_read(bdev->bd_inode);
306         if (i_size == 0)
307                 goto out_exit;
308         info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
309         if (info == NULL)
310                 goto out_exit;
311         geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL);
312         if (geo == NULL)
313                 goto out_nogeo;
314         label = kmalloc(sizeof(union label_t), GFP_KERNEL);
315         if (label == NULL)
316                 goto out_nolab;
317         if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
318                 goto out_freeall;
319         if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0) {
320                 kfree(info);
321                 info = NULL;
322         }
323
324         if (find_label(state, info, geo, blocksize, &labelsect, name, type,
325                        label)) {
326                 if (!strncmp(type, "VOL1", 4)) {
327                         res = find_vol1_partitions(state, geo, blocksize, name,
328                                                    label);
329                 } else if (!strncmp(type, "LNX1", 4)) {
330                         res = find_lnx1_partitions(state, geo, blocksize, name,
331                                                    label, labelsect, i_size,
332                                                    info);
333                 } else if (!strncmp(type, "CMS1", 4)) {
334                         res = find_cms1_partitions(state, geo, blocksize, name,
335                                                    label, labelsect);
336                 }
337         } else if (info) {
338                 /*
339                  * ugly but needed for backward compatibility:
340                  * If the block device is a DASD (i.e. BIODASDINFO2 works),
341                  * then we claim it in any case, even though it has no valid
342                  * label. If it has the LDL format, then we simply define a
343                  * partition as if it had an LNX1 label.
344                  */
345                 res = 1;
346                 if (info->format == DASD_FORMAT_LDL) {
347                         strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
348                         size = i_size >> 9;
349                         offset = (info->label_block + 1) * (blocksize >> 9);
350                         put_partition(state, 1, offset, size-offset);
351                         strlcat(state->pp_buf, "\n", PAGE_SIZE);
352                 }
353         } else
354                 res = 0;
355
356 out_freeall:
357         kfree(label);
358 out_nolab:
359         kfree(geo);
360 out_nogeo:
361         kfree(info);
362 out_exit:
363         return res;
364 }