Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / lvm2 / dist / lib / device / device.c
blob8142d06c340da9df824eeba0b6567736ee217ef0
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
7 * This file is part of LVM2.
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.
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
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"
26 /* See linux/genhd.h and fs/partitions/msdos */
28 #define PART_MAGIC 0xAA55
29 #define PART_MAGIC_OFFSET UINT64_C(0x1FE)
30 #define PART_OFFSET UINT64_C(0x1BE)
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));
45 static int _is_partitionable(struct device *dev)
47 int parts = max_partitions(MAJOR(dev->dev));
49 /* All MD devices are partitionable via blkext (as of 2.6.28) */
50 if (MAJOR(dev->dev) == md_major())
51 return 1;
53 if ((parts <= 1) || (MINOR(dev->dev) % parts))
54 return 0;
56 return 1;
59 static int _has_partition_table(struct device *dev)
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;
67 if (!dev_open(dev)) {
68 stack;
69 return -1;
72 if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
73 goto_out;
75 /* FIXME Check for other types of partition table too */
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;
87 /* Must have at least one non-empty partition */
88 if (part->nr_sects)
89 ret = 1;
93 out:
94 if (!dev_close(dev))
95 stack;
97 return ret;
100 int is_partitioned_dev(struct device *dev)
102 if (!_is_partitionable(dev))
103 return 0;
105 return _has_partition_table(dev);
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>
116 #include <errno.h>
117 #include <sys/ioctl.h>
118 #include <linux/fs.h>
119 #include <linux/major.h>
120 #include <linux/genhd.h>
122 int _get_partition_type(struct dev_filter *filter, struct device *d);
124 #define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
126 int is_extended_partition(struct device *d)
128 return (MINOR_PART(d) > 4) ? 1 : 0;
131 struct device *dev_primary(struct dev_mgr *dm, struct device *d)
133 struct device *ret;
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));
142 return ret;
146 int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
148 int pt;
150 pt = _get_partition_type(dm, d);
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;
164 if (is_whole_disk(dm, d)) {
165 log_error("%s: looks to possess partition table", d->name);
166 return 0;
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;
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;
183 return 1;
186 int _get_partition_type(struct dev_mgr *dm, struct device *d)
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;
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;
210 if (!(buffer = dm_malloc(SECTOR_SIZE))) {
211 log_error("Failed to allocate partition table buffer");
212 return 0;
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;
222 s_buffer = (unsigned short *) buffer;
223 part = (struct partition *) (buffer + 0x1be);
224 part_sought = MINOR_PART(dm, d);
226 do {
227 bytes_read = 0;
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;
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;
241 if (read_ret == -1) {
242 log_error("%s: read failed: %s", primary->name,
243 strerror(errno));
244 return 0;
247 if (s_buffer[255] == 0xAA55) {
248 if (is_whole_disk(dm, d))
249 return -1;
250 } else
251 return 0;
253 extended_partition = 0;
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;
272 } else
273 part_found++;
274 } else if (!part[p].sys_ind)
275 part_found++;
277 if (part_sought == part_found)
278 return part[p].sys_ind;
281 first_partition = 0;
283 while (extended_partition == 1);
285 return 0;
287 #endif
289 #ifdef linux
291 int get_primary_dev(const char *sysfs_dir,
292 struct device *dev, dev_t *result)
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;
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;
309 if (stat(path, &info) == -1) {
310 if (errno != ENOENT)
311 log_sys_error("stat", path);
312 return ret;
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
322 if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
323 log_sys_error("readlink", path);
324 return ret;
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;
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;
342 fp = fopen(path, "r");
343 if (!fp) {
344 log_sys_error("fopen", path);
345 return ret;
348 if (!fgets(buffer, sizeof(buffer), fp)) {
349 log_sys_error("fgets", path);
350 goto out;
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;
358 *result = MKDEV(pri_maj, pri_min);
359 ret = 1;
361 out:
362 if (fclose(fp))
363 log_sys_error("fclose", path);
365 return ret;
368 static unsigned long _dev_topology_attribute(const char *attribute,
369 const char *sysfs_dir,
370 struct device *dev)
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;
379 if (!attribute || !*attribute)
380 return_0;
382 if (!sysfs_dir || !*sysfs_dir)
383 return_0;
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;
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
397 if (stat(path, &info) == -1) {
398 if (errno != ENOENT) {
399 log_sys_error("stat", path);
400 return 0;
402 if (!get_primary_dev(sysfs_dir, dev, &primary))
403 return 0;
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;
412 if (stat(path, &info) == -1) {
413 if (errno != ENOENT)
414 log_sys_error("stat", path);
415 return 0;
419 if (!(fp = fopen(path, "r"))) {
420 log_sys_error("fopen", path);
421 return 0;
424 if (!fgets(buffer, sizeof(buffer), fp)) {
425 log_sys_error("fgets", path);
426 goto out;
429 if (sscanf(buffer, "%lu", &result) != 1) {
430 log_error("sysfs file %s not in expected format: %s", path,
431 buffer);
432 goto out;
435 log_very_verbose("Device %s %s is %lu bytes.",
436 dev_name(dev), attribute, result);
438 out:
439 if (fclose(fp))
440 log_sys_error("fclose", path);
442 return result >> SECTOR_SHIFT;
445 unsigned long dev_alignment_offset(const char *sysfs_dir,
446 struct device *dev)
448 return _dev_topology_attribute("alignment_offset",
449 sysfs_dir, dev);
452 unsigned long dev_minimum_io_size(const char *sysfs_dir,
453 struct device *dev)
455 return _dev_topology_attribute("queue/minimum_io_size",
456 sysfs_dir, dev);
459 unsigned long dev_optimal_io_size(const char *sysfs_dir,
460 struct device *dev)
462 return _dev_topology_attribute("queue/optimal_io_size",
463 sysfs_dir, dev);
466 #else
468 int get_primary_dev(const char *sysfs_dir,
469 struct device *dev, dev_t *result)
471 return 0;
474 unsigned long dev_alignment_offset(const char *sysfs_dir,
475 struct device *dev)
477 return 0UL;
480 unsigned long dev_minimum_io_size(const char *sysfs_dir,
481 struct device *dev)
483 return 0UL;
486 unsigned long dev_optimal_io_size(const char *sysfs_dir,
487 struct device *dev)
489 return 0UL;
492 #endif