xref: /netbsd-src/external/gpl2/lvm2/dist/lib/device/device.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*	$NetBSD: device.c,v 1.1.1.1 2008/12/22 00:17:57 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 "lib.h"
19 #include "lvm-types.h"
20 #include "device.h"
21 #include "metadata.h"
22 #include "filter.h"
23 #include "xlate.h"
24 
25 /* See linux/genhd.h and fs/partitions/msdos */
26 
27 #define PART_MAGIC 0xAA55
28 #define PART_MAGIC_OFFSET UINT64_C(0x1FE)
29 #define PART_OFFSET UINT64_C(0x1BE)
30 
31 struct partition {
32 	uint8_t boot_ind;
33 	uint8_t head;
34 	uint8_t sector;
35 	uint8_t cyl;
36 	uint8_t sys_ind;	/* partition type */
37 	uint8_t end_head;
38 	uint8_t end_sector;
39 	uint8_t end_cyl;
40 	uint32_t start_sect;
41 	uint32_t nr_sects;
42 } __attribute__((packed));
43 
44 static int _is_partitionable(struct device *dev)
45 {
46 	int parts = max_partitions(MAJOR(dev->dev));
47 
48 	if ((parts <= 1) || (MINOR(dev->dev) % parts))
49 		return 0;
50 
51 	return 1;
52 }
53 
54 static int _has_partition_table(struct device *dev)
55 {
56 	int ret = 0;
57 	unsigned p;
58 	uint16_t buf[SECTOR_SIZE/sizeof(uint16_t)];
59 	uint16_t *part_magic;
60 	struct partition *part;
61 
62 	if (!dev_open(dev)) {
63 		stack;
64 		return -1;
65 	}
66 
67 	if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
68 		goto_out;
69 
70 	/* FIXME Check for other types of partition table too */
71 
72 	/* Check for msdos partition table */
73 	part_magic = buf + PART_MAGIC_OFFSET/sizeof(buf[0]);
74 	if ((*part_magic == xlate16(PART_MAGIC))) {
75 		part = (struct partition *) (buf + PART_OFFSET/sizeof(buf[0]));
76 		for (p = 0; p < 4; p++, part++) {
77 			/* Table is invalid if boot indicator not 0 or 0x80 */
78 			if ((part->boot_ind & 0x7f)) {
79 				ret = 0;
80 				break;
81 			}
82 			/* Must have at least one non-empty partition */
83 			if (part->nr_sects)
84 				ret = 1;
85 		}
86 	}
87 
88       out:
89 	if (!dev_close(dev))
90 		stack;
91 
92 	return ret;
93 }
94 
95 int is_partitioned_dev(struct device *dev)
96 {
97 	if (!_is_partitionable(dev))
98 		return 0;
99 
100 	return _has_partition_table(dev);
101 }
102 
103 #if 0
104 #include <sys/stat.h>
105 #include <sys/mman.h>
106 #include <stdio.h>
107 #include <unistd.h>
108 #include <fcntl.h>
109 #include <ctype.h>
110 
111 #include <errno.h>
112 #include <sys/ioctl.h>
113 #include <linux/fs.h>
114 #include <linux/major.h>
115 #include <linux/genhd.h>
116 
117 int _get_partition_type(struct dev_filter *filter, struct device *d);
118 
119 #define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
120 
121 int is_extended_partition(struct device *d)
122 {
123 	return (MINOR_PART(d) > 4) ? 1 : 0;
124 }
125 
126 struct device *dev_primary(struct dev_mgr *dm, struct device *d)
127 {
128 	struct device *ret;
129 
130 	ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
131 	/* FIXME: Needs replacing with a 'refresh' */
132 	if (!ret) {
133 		init_dev_scan(dm);
134 		ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
135 	}
136 
137 	return ret;
138 
139 }
140 
141 int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
142 {
143 	int pt;
144 
145 	pt = _get_partition_type(dm, d);
146 
147 	if (!pt) {
148 		if (is_whole_disk(dm, d))
149 			/* FIXME: Overloaded pt=0 in error cases */
150 			return 1;
151 		else {
152 			log_error
153 			    ("%s: missing partition table "
154 			     "on partitioned device", d->name);
155 			return 0;
156 		}
157 	}
158 
159 	if (is_whole_disk(dm, d)) {
160 		log_error("%s: looks to possess partition table", d->name);
161 		return 0;
162 	}
163 
164 	/* check part type */
165 	if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
166 		log_error("%s: invalid partition type 0x%x "
167 			  "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
168 		return 0;
169 	}
170 
171 	if (pt == LVM_PARTITION) {
172 		log_error
173 		    ("%s: old LVM partition type found - please change to 0x%x",
174 		     d->name, LVM_NEW_PARTITION);
175 		return 0;
176 	}
177 
178 	return 1;
179 }
180 
181 int _get_partition_type(struct dev_mgr *dm, struct device *d)
182 {
183 	int pv_handle = -1;
184 	struct device *primary;
185 	ssize_t read_ret;
186 	ssize_t bytes_read = 0;
187 	char *buffer;
188 	unsigned short *s_buffer;
189 	struct partition *part;
190 	loff_t offset = 0;
191 	loff_t extended_offset = 0;
192 	int part_sought;
193 	int part_found = 0;
194 	int first_partition = 1;
195 	int extended_partition = 0;
196 	int p;
197 
198 	if (!(primary = dev_primary(dm, d))) {
199 		log_error
200 		    ("Failed to find main device containing partition %s",
201 		     d->name);
202 		return 0;
203 	}
204 
205 	if (!(buffer = dm_malloc(SECTOR_SIZE))) {
206 		log_error("Failed to allocate partition table buffer");
207 		return 0;
208 	}
209 
210 	/* Get partition table */
211 	if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
212 		log_error("%s: open failed: %s", primary->name,
213 			  strerror(errno));
214 		return 0;
215 	}
216 
217 	s_buffer = (unsigned short *) buffer;
218 	part = (struct partition *) (buffer + 0x1be);
219 	part_sought = MINOR_PART(dm, d);
220 
221 	do {
222 		bytes_read = 0;
223 
224 		if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
225 			log_error("%s: llseek failed: %s",
226 				  primary->name, strerror(errno));
227 			return 0;
228 		}
229 
230 		while ((bytes_read < SECTOR_SIZE) &&
231 		       (read_ret =
232 			read(pv_handle, buffer + bytes_read,
233 			     SECTOR_SIZE - bytes_read)) != -1)
234 			bytes_read += read_ret;
235 
236 		if (read_ret == -1) {
237 			log_error("%s: read failed: %s", primary->name,
238 				  strerror(errno));
239 			return 0;
240 		}
241 
242 		if (s_buffer[255] == 0xAA55) {
243 			if (is_whole_disk(dm, d))
244 				return -1;
245 		} else
246 			return 0;
247 
248 		extended_partition = 0;
249 
250 		/* Loop through primary partitions */
251 		for (p = 0; p < 4; p++) {
252 			if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
253 			    part[p].sys_ind == LINUX_EXTENDED_PARTITION
254 			    || part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
255 				extended_partition = 1;
256 				offset = extended_offset + part[p].start_sect;
257 				if (extended_offset == 0)
258 					extended_offset = part[p].start_sect;
259 				if (first_partition == 1)
260 					part_found++;
261 			} else if (first_partition == 1) {
262 				if (p == part_sought) {
263 					if (part[p].sys_ind == 0) {
264 						/* missing primary? */
265 						return 0;
266 					}
267 				} else
268 					part_found++;
269 			} else if (!part[p].sys_ind)
270 				part_found++;
271 
272 			if (part_sought == part_found)
273 				return part[p].sys_ind;
274 
275 		}
276 		first_partition = 0;
277 	}
278 	while (extended_partition == 1);
279 
280 	return 0;
281 }
282 #endif
283