blob: f7907b20b4d576df0b08c0058d956a5ae6a3fafd [file] [log] [blame]
The Android Open Source Project8ac3a132009-01-20 14:04:01 -08001
2/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <stdlib.h>
19#include <string.h>
20#include <dirent.h>
21#include <errno.h>
22#include <fcntl.h>
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <sys/mman.h>
28
29#include <linux/fs.h>
30
31#include "vold.h"
32#include "blkdev.h"
33#include "diskmbr.h"
34
35#define DEBUG_BLKDEV 0
36
37static blkdev_list_t *list_root = NULL;
38
39static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
40 int minor, char *type, struct media *media, char *dev_fspath);
41static void blkdev_dev_fspath_set(blkdev_t *blk, char *dev_fspath);
42
43int blkdev_handle_devicefile_removed(blkdev_t *blk, char *dev_fspath)
44{
45#if DEBUG_BLKDEV
46 LOG_VOL("blkdev_handle_devicefile_removed(%s):\n", dev_fspath);
47#endif
48 blkdev_dev_fspath_set(blk, NULL);
49 return 0;
50}
51
52int blkdev_handle_devicefile_created(blkdev_t *blk, char *dev_fspath)
53{
54 int rc = 0;
55 blkdev_t *disk;
56
57#if DEBUG_BLKDEV
58 LOG_VOL("blkdev_handle_devicefile_created(%s):\n", dev_fspath);
59#endif
60
61 if (!blk) {
62 /*
63 * This device does not yet have a backing blkdev associated with it.
64 * Create a new one in the pending state and fill in the information
65 * we have.
66 */
67 struct stat sbuf;
68
69 if (stat(dev_fspath, &sbuf) < 0) {
70 LOGE("Unable to stat device '%s' (%s)\n", dev_fspath, strerror(errno));
71 return -errno;
72 }
73
74 int major = (sbuf.st_rdev & 0xfff00) >> 8;
75 int minor = (sbuf.st_rdev & 0xff) | ((sbuf.st_rdev >> 12) & 0xfff00);
76
77 disk = blkdev_lookup_by_devno(major, 0);
78
79 if (!disk) {
80 /*
81 * If there isn't a disk associated with this device, then
82 * its not what we're looking for
83 */
84#if DEBUG_BLKDEV
85 LOG_VOL("Ignoring device file '%s' (no disk found)\n", dev_fspath);
86#endif
87 return 0;
88 }
89
90 if (!(blk = blkdev_create_pending_partition(disk, dev_fspath, major,
91 minor, disk->media))) {
92 LOGE("Unable to create pending blkdev\n");
93 return -1;
94 }
95 } else
96 blkdev_dev_fspath_set(blk, dev_fspath);
97
98 /*
99 * If we're a disk, then read the partition table. Otherwise we're
100 * a partition so get the partition type
101 */
102 disk = blk->disk;
103
104 int fd;
105
106 if ((fd = open(disk->dev_fspath, O_RDWR)) < 0) {
107 LOGE("Unable to open device '%s' (%s)\n", disk->dev_fspath, strerror(errno));
108 return -errno;
109 }
110
111 if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) {
112 LOGE("Unable to get device size (%m)\n");
113 return -errno;
114 }
115
116#if DEBUG_BLKDEV
117 LOG_VOL("New device '%s' size = %u sectors\n", dev_fspath, blk->nr_sec);
118#endif
119
120 void *raw_pt;
121 unsigned char *chr_pt;
122 int i;
123
124 raw_pt = chr_pt = mmap(NULL, 512, PROT_READ, MAP_PRIVATE, fd, 0);
125 if (raw_pt == MAP_FAILED) {
126 LOGE("Unable to mmap device paritition table (%m)\n");
127 goto out_nommap;
128 }
129
130 if (blk->type == blkdev_disk) {
131 blk->nr_parts = 0;
132
133 if ((chr_pt[0x1fe] != 0x55) && (chr_pt[0x1ff] != 0xAA)) {
134 LOG_VOL("Disk '%s' does not contain a partition table\n", dev_fspath);
135 goto out;
136 }
137
138 for (i = 0; i < 4; i++) {
139 struct dos_partition part;
140
141 dos_partition_dec(raw_pt + DOSPARTOFF + i * sizeof(struct dos_partition), &part);
142 if (part.dp_size != 0 && part.dp_typ != 0)
143 blk->nr_parts++;
144 }
145 LOG_VOL("Disk device '%s' (blkdev %s) contains %d partitions\n",
146 dev_fspath, blk->devpath, blk->nr_parts);
147 } else if (blk->type == blkdev_partition) {
148 struct dos_partition part;
149 int part_no = blk->minor -1;
150
151 dos_partition_dec(raw_pt + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part);
152
153 if (!part.dp_typ)
154 LOG_VOL("Warning - Partition device '%s' (blkdev %s) has no partition type set\n",
155 dev_fspath, blk->devpath);
156 blk->part_type = part.dp_typ;
157
158 LOG_VOL("Partition device '%s' (blkdev %s) partition type 0x%x\n",
159 dev_fspath, blk->devpath, blk->part_type);
160 } else {
161 LOGE("Bad blkdev type '%d'\n", blk->type);
162 rc = -EINVAL;
163 goto out;
164 }
165
166 out:
167 munmap(raw_pt, 512);
168 out_nommap:
169 close(fd);
170 return rc;
171}
172
173blkdev_t *blkdev_create_pending_partition(blkdev_t *disk, char *dev_fspath, int major,
174 int minor, struct media *media)
175{
176 return _blkdev_create(disk, NULL, major, minor, "partition", media, dev_fspath);
177}
178
179blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type)
180{
181 return _blkdev_create(disk, devpath, major, minor, type, media, NULL);
182}
183
184static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
185 int minor, char *type, struct media *media, char *dev_fspath)
186{
187 blkdev_t *new;
188 struct blkdev_list *list_entry;
189
190 if (disk && disk->type != blkdev_disk) {
191 LOGE("Non disk parent specified for blkdev!\n");
192 return NULL;
193 }
194
195 if (!(new = malloc(sizeof(blkdev_t))))
196 return NULL;
197
198 memset(new, 0, sizeof(blkdev_t));
199
200 if (!(list_entry = malloc(sizeof(struct blkdev_list)))) {
201 free (new);
202 return NULL;
203 }
204 list_entry->dev = new;
205 list_entry->next = NULL;
206
207 if (!list_root)
208 list_root = list_entry;
209 else {
210 struct blkdev_list *list_scan = list_root;
211 while (list_scan->next)
212 list_scan = list_scan->next;
213 list_scan->next = list_entry;
214 }
215
216 if (devpath)
217 new->devpath = strdup(devpath);
218 new->major = major;
219 new->minor = minor;
220 new->media = media;
221 if (dev_fspath)
222 new->dev_fspath = strdup(dev_fspath);
223 new->nr_sec = 0xffffffff;
224
225 if (disk)
226 new->disk = disk;
227 else
228 new->disk = new; // Note the self disk pointer
229
230 if (!strcmp(type, "disk"))
231 new->type = blkdev_disk;
232 else if (!strcmp(type, "partition"))
233 new->type = blkdev_partition;
234 else {
235 LOGE("Unknown block device type '%s'\n", type);
236 new->type = blkdev_unknown;
237 }
238
239 return new;
240}
241
242void blkdev_destroy(blkdev_t *blkdev)
243{
244 struct blkdev_list *list_next;
245
246 if (list_root->dev == blkdev) {
247 list_next = list_root->next;
248 free (list_root);
249 list_root = list_next;
250 } else {
251 struct blkdev_list *list_scan = list_root;
252 while (list_scan->next->dev != blkdev)
253 list_scan = list_scan -> next;
254 list_next = list_scan->next->next;
255 free(list_scan->next);
256 list_scan->next = list_next;
257 }
258
259 if (blkdev->devpath)
260 free(blkdev->devpath);
261 if (blkdev->dev_fspath)
262 free(blkdev->dev_fspath);
263 free(blkdev);
264}
265
266blkdev_t *blkdev_lookup_by_path(char *devpath)
267{
268 struct blkdev_list *list_scan = list_root;
269
270 while (list_scan) {
271 if (!strcmp(list_scan->dev->devpath, devpath))
272 return list_scan->dev;
273 list_scan = list_scan->next;
274 }
275#if DEBUG_BLKDEV
276 LOG_VOL("blkdev_lookup_by_path(): No blkdev found @ %s\n", devpath);
277#endif
278 return NULL;
279}
280
281blkdev_t *blkdev_lookup_by_devno(int maj, int min)
282{
283 struct blkdev_list *list_scan = list_root;
284
285 while (list_scan) {
286 if ((list_scan->dev->major == maj) &&
287 (list_scan->dev->minor == min))
288 return list_scan->dev;
289 list_scan = list_scan->next;
290 }
291#if DEBUG_BLKDEV
292 LOG_VOL("blkdev_lookup_by_devno(): No blkdev found for %d.%d\n", maj, min);
293#endif
294 return NULL;
295}
296
297blkdev_t *blkdev_lookup_by_dev_fspath(char *dev_fspath)
298{
299 struct blkdev_list *list_scan = list_root;
300
301 while (list_scan) {
302 if (list_scan->dev->dev_fspath) {
303 if (!strcmp(list_scan->dev->dev_fspath, dev_fspath))
304 return list_scan->dev;
305 }
306
307 list_scan = list_scan->next;
308 }
309// LOG_VOL("blkdev_lookup_by_devno(): No blkdev found for %d.%d\n", maj, min);
310 return NULL;
311}
312
313
314/*
315 * Given a disk device, return the number of partitions yet to be
316 * processed.
317 */
318int blkdev_get_num_pending_partitions(blkdev_t *blk)
319{
320 struct blkdev_list *list_scan = list_root;
321 int num = blk->nr_parts;
322
323 if (blk->type != blkdev_disk)
324 return -EINVAL;
325
326 while (list_scan) {
327 if (list_scan->dev->type != blkdev_partition)
328 goto next;
329
330 if (list_scan->dev->major != blk->major)
331 goto next;
332
333 if (list_scan->dev->nr_sec != 0xffffffff)
334 num--;
335 next:
336 list_scan = list_scan->next;
337 }
338 return num;
339}
340
341void blkdev_devpath_set(blkdev_t *blk, char *devpath)
342{
343 blk->devpath = strdup(devpath);
344}
345
346static void blkdev_dev_fspath_set(blkdev_t *blk, char *dev_fspath)
347{
348 if (dev_fspath)
349 blk->dev_fspath = strdup(dev_fspath);
350 else {
351 free(blk->dev_fspath);
352 blk->dev_fspath = NULL;
353 }
354}