Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / lvm2 / dist / lib / device / device.c
1 /*      $NetBSD: device.c,v 1.1.1.2 2009/12/02 00:26:34 haad Exp $      */
2
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #include <libgen.h> /* dirname, basename */
19 #include "lib.h"
20 #include "lvm-types.h"
21 #include "device.h"
22 #include "metadata.h"
23 #include "filter.h"
24 #include "xlate.h"
25
26 /* See linux/genhd.h and fs/partitions/msdos */
27
28 #define PART_MAGIC 0xAA55
29 #define PART_MAGIC_OFFSET UINT64_C(0x1FE)
30 #define PART_OFFSET UINT64_C(0x1BE)
31
32 struct partition {
33         uint8_t boot_ind;
34         uint8_t head;
35         uint8_t sector;
36         uint8_t cyl;
37         uint8_t sys_ind;        /* partition type */
38         uint8_t end_head;
39         uint8_t end_sector;
40         uint8_t end_cyl;
41         uint32_t start_sect;
42         uint32_t nr_sects;
43 } __attribute__((packed));
44
45 static int _is_partitionable(struct device *dev)
46 {
47         int parts = max_partitions(MAJOR(dev->dev));
48
49         /* All MD devices are partitionable via blkext (as of 2.6.28) */
50         if (MAJOR(dev->dev) == md_major())
51                 return 1;
52
53         if ((parts <= 1) || (MINOR(dev->dev) % parts))
54                 return 0;
55
56         return 1;
57 }
58
59 static int _has_partition_table(struct device *dev)
60 {
61         int ret = 0;
62         unsigned p;
63         uint16_t buf[SECTOR_SIZE/sizeof(uint16_t)];
64         uint16_t *part_magic;
65         struct partition *part;
66
67         if (!dev_open(dev)) {
68                 stack;
69                 return -1;
70         }
71
72         if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
73                 goto_out;
74
75         /* FIXME Check for other types of partition table too */
76
77         /* Check for msdos partition table */
78         part_magic = buf + PART_MAGIC_OFFSET/sizeof(buf[0]);
79         if ((*part_magic == xlate16(PART_MAGIC))) {
80                 part = (struct partition *) (buf + PART_OFFSET/sizeof(buf[0]));
81                 for (p = 0; p < 4; p++, part++) {
82                         /* Table is invalid if boot indicator not 0 or 0x80 */
83                         if ((part->boot_ind & 0x7f)) {
84                                 ret = 0;
85                                 break;
86                         }
87                         /* Must have at least one non-empty partition */
88                         if (part->nr_sects)
89                                 ret = 1;
90                 }
91         }
92
93       out:
94         if (!dev_close(dev))
95                 stack;
96
97         return ret;
98 }
99
100 int is_partitioned_dev(struct device *dev)
101 {
102         if (!_is_partitionable(dev))
103                 return 0;
104
105         return _has_partition_table(dev);
106 }
107
108 #if 0
109 #include <sys/stat.h>
110 #include <sys/mman.h>
111 #include <stdio.h>
112 #include <unistd.h>
113 #include <fcntl.h>
114 #include <ctype.h>
115
116 #include <errno.h>
117 #include <sys/ioctl.h>
118 #include <linux/fs.h>
119 #include <linux/major.h>
120 #include <linux/genhd.h>
121
122 int _get_partition_type(struct dev_filter *filter, struct device *d);
123
124 #define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
125
126 int is_extended_partition(struct device *d)
127 {
128         return (MINOR_PART(d) > 4) ? 1 : 0;
129 }
130
131 struct device *dev_primary(struct dev_mgr *dm, struct device *d)
132 {
133         struct device *ret;
134
135         ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
136         /* FIXME: Needs replacing with a 'refresh' */
137         if (!ret) {
138                 init_dev_scan(dm);
139                 ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
140         }
141
142         return ret;
143
144 }
145
146 int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
147 {
148         int pt;
149
150         pt = _get_partition_type(dm, d);
151
152         if (!pt) {
153                 if (is_whole_disk(dm, d))
154                         /* FIXME: Overloaded pt=0 in error cases */
155                         return 1;
156                 else {
157                         log_error
158                             ("%s: missing partition table "
159                              "on partitioned device", d->name);
160                         return 0;
161                 }
162         }
163
164         if (is_whole_disk(dm, d)) {
165                 log_error("%s: looks to possess partition table", d->name);
166                 return 0;
167         }
168
169         /* check part type */
170         if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
171                 log_error("%s: invalid partition type 0x%x "
172                           "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
173                 return 0;
174         }
175
176         if (pt == LVM_PARTITION) {
177                 log_error
178                     ("%s: old LVM partition type found - please change to 0x%x",
179                      d->name, LVM_NEW_PARTITION);
180                 return 0;
181         }
182
183         return 1;
184 }
185
186 int _get_partition_type(struct dev_mgr *dm, struct device *d)
187 {
188         int pv_handle = -1;
189         struct device *primary;
190         ssize_t read_ret;
191         ssize_t bytes_read = 0;
192         char *buffer;
193         unsigned short *s_buffer;
194         struct partition *part;
195         loff_t offset = 0;
196         loff_t extended_offset = 0;
197         int part_sought;
198         int part_found = 0;
199         int first_partition = 1;
200         int extended_partition = 0;
201         int p;
202
203         if (!(primary = dev_primary(dm, d))) {
204                 log_error
205                     ("Failed to find main device containing partition %s",
206                      d->name);
207                 return 0;
208         }
209
210         if (!(buffer = dm_malloc(SECTOR_SIZE))) {
211                 log_error("Failed to allocate partition table buffer");
212                 return 0;
213         }
214
215         /* Get partition table */
216         if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
217                 log_error("%s: open failed: %s", primary->name,
218                           strerror(errno));
219                 return 0;
220         }
221
222         s_buffer = (unsigned short *) buffer;
223         part = (struct partition *) (buffer + 0x1be);
224         part_sought = MINOR_PART(dm, d);
225
226         do {
227                 bytes_read = 0;
228
229                 if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
230                         log_error("%s: llseek failed: %s",
231                                   primary->name, strerror(errno));
232                         return 0;
233                 }
234
235                 while ((bytes_read < SECTOR_SIZE) &&
236                        (read_ret =
237                         read(pv_handle, buffer + bytes_read,
238                              SECTOR_SIZE - bytes_read)) != -1)
239                         bytes_read += read_ret;
240
241                 if (read_ret == -1) {
242                         log_error("%s: read failed: %s", primary->name,
243                                   strerror(errno));
244                         return 0;
245                 }
246
247                 if (s_buffer[255] == 0xAA55) {
248                         if (is_whole_disk(dm, d))
249                                 return -1;
250                 } else
251                         return 0;
252
253                 extended_partition = 0;
254
255                 /* Loop through primary partitions */
256                 for (p = 0; p < 4; p++) {
257                         if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
258                             part[p].sys_ind == LINUX_EXTENDED_PARTITION
259                             || part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
260                                 extended_partition = 1;
261                                 offset = extended_offset + part[p].start_sect;
262                                 if (extended_offset == 0)
263                                         extended_offset = part[p].start_sect;
264                                 if (first_partition == 1)
265                                         part_found++;
266                         } else if (first_partition == 1) {
267                                 if (p == part_sought) {
268                                         if (part[p].sys_ind == 0) {
269                                                 /* missing primary? */
270                                                 return 0;
271                                         }
272                                 } else
273                                         part_found++;
274                         } else if (!part[p].sys_ind)
275                                 part_found++;
276
277                         if (part_sought == part_found)
278                                 return part[p].sys_ind;
279
280                 }
281                 first_partition = 0;
282         }
283         while (extended_partition == 1);
284
285         return 0;
286 }
287 #endif
288
289 #ifdef linux
290
291 int get_primary_dev(const char *sysfs_dir,
292                     struct device *dev, dev_t *result)
293 {
294         char path[PATH_MAX+1];
295         char temp_path[PATH_MAX+1];
296         char buffer[64];
297         struct stat info;
298         FILE *fp;
299         uint32_t pri_maj, pri_min;
300         int ret = 0;
301
302         /* check if dev is a partition */
303         if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/partition",
304                         sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
305                 log_error("dm_snprintf partition failed");
306                 return ret;
307         }
308
309         if (stat(path, &info) == -1) {
310                 if (errno != ENOENT)
311                         log_sys_error("stat", path);
312                 return ret;
313         }
314
315         /*
316          * extract parent's path from the partition's symlink, e.g.:
317          * - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1
318          * - dirname ../../block/md0/md0p1 = ../../block/md0
319          * - basename ../../block/md0/md0  = md0
320          * Parent's 'dev' sysfs attribute  = /sys/block/md0/dev
321          */
322         if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
323                 log_sys_error("readlink", path);
324                 return ret;
325         }
326
327         if (dm_snprintf(path, PATH_MAX, "%s/block/%s/dev",
328                         sysfs_dir, basename(dirname(temp_path))) < 0) {
329                 log_error("dm_snprintf dev failed");
330                 return ret;
331         }
332
333         /* finally, parse 'dev' attribute and create corresponding dev_t */
334         if (stat(path, &info) == -1) {
335                 if (errno == ENOENT)
336                         log_error("sysfs file %s does not exist", path);
337                 else
338                         log_sys_error("stat", path);
339                 return ret;
340         }
341
342         fp = fopen(path, "r");
343         if (!fp) {
344                 log_sys_error("fopen", path);
345                 return ret;
346         }
347
348         if (!fgets(buffer, sizeof(buffer), fp)) {
349                 log_sys_error("fgets", path);
350                 goto out;
351         }
352
353         if (sscanf(buffer, "%d:%d", &pri_maj, &pri_min) != 2) {
354                 log_error("sysfs file %s not in expected MAJ:MIN format: %s",
355                           path, buffer);
356                 goto out;
357         }
358         *result = MKDEV(pri_maj, pri_min);
359         ret = 1;
360
361 out:
362         if (fclose(fp))
363                 log_sys_error("fclose", path);
364
365         return ret;
366 }
367
368 static unsigned long _dev_topology_attribute(const char *attribute,
369                                              const char *sysfs_dir,
370                                              struct device *dev)
371 {
372         const char *sysfs_fmt_str = "%s/dev/block/%d:%d/%s";
373         char path[PATH_MAX+1], buffer[64];
374         FILE *fp;
375         struct stat info;
376         dev_t uninitialized_var(primary);
377         unsigned long result = 0UL;
378
379         if (!attribute || !*attribute)
380                 return_0;
381
382         if (!sysfs_dir || !*sysfs_dir)
383                 return_0;
384
385         if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
386                         (int)MAJOR(dev->dev), (int)MINOR(dev->dev),
387                         attribute) < 0) {
388                 log_error("dm_snprintf %s failed", attribute);
389                 return 0;
390         }
391
392         /*
393          * check if the desired sysfs attribute exists
394          * - if not: either the kernel doesn't have topology support
395          *   or the device could be a partition
396          */
397         if (stat(path, &info) == -1) {
398                 if (errno != ENOENT) {
399                         log_sys_error("stat", path);
400                         return 0;
401                 }
402                 if (!get_primary_dev(sysfs_dir, dev, &primary))
403                         return 0;
404
405                 /* get attribute from partition's primary device */
406                 if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
407                                 (int)MAJOR(primary), (int)MINOR(primary),
408                                 attribute) < 0) {
409                         log_error("primary dm_snprintf %s failed", attribute);
410                         return 0;
411                 }
412                 if (stat(path, &info) == -1) {
413                         if (errno != ENOENT)
414                                 log_sys_error("stat", path);
415                         return 0;
416                 }
417         }
418
419         if (!(fp = fopen(path, "r"))) {
420                 log_sys_error("fopen", path);
421                 return 0;
422         }
423
424         if (!fgets(buffer, sizeof(buffer), fp)) {
425                 log_sys_error("fgets", path);
426                 goto out;
427         }
428
429         if (sscanf(buffer, "%lu", &result) != 1) {
430                 log_error("sysfs file %s not in expected format: %s", path,
431                           buffer);
432                 goto out;
433         }
434
435         log_very_verbose("Device %s %s is %lu bytes.",
436                          dev_name(dev), attribute, result);
437
438 out:
439         if (fclose(fp))
440                 log_sys_error("fclose", path);
441
442         return result >> SECTOR_SHIFT;
443 }
444
445 unsigned long dev_alignment_offset(const char *sysfs_dir,
446                                    struct device *dev)
447 {
448         return _dev_topology_attribute("alignment_offset",
449                                        sysfs_dir, dev);
450 }
451
452 unsigned long dev_minimum_io_size(const char *sysfs_dir,
453                                   struct device *dev)
454 {
455         return _dev_topology_attribute("queue/minimum_io_size",
456                                        sysfs_dir, dev);
457 }
458
459 unsigned long dev_optimal_io_size(const char *sysfs_dir,
460                                   struct device *dev)
461 {
462         return _dev_topology_attribute("queue/optimal_io_size",
463                                        sysfs_dir, dev);
464 }
465
466 #else
467
468 int get_primary_dev(const char *sysfs_dir,
469                     struct device *dev, dev_t *result)
470 {
471         return 0;
472 }
473
474 unsigned long dev_alignment_offset(const char *sysfs_dir,
475                                    struct device *dev)
476 {
477         return 0UL;
478 }
479
480 unsigned long dev_minimum_io_size(const char *sysfs_dir,
481                                   struct device *dev)
482 {
483         return 0UL;
484 }
485
486 unsigned long dev_optimal_io_size(const char *sysfs_dir,
487                                   struct device *dev)
488 {
489         return 0UL;
490 }
491
492 #endif