blob: 3167507607b040dd41e09f06a86442bcb0e6296e [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -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#include <linux/msdos_fs.h>
31
32#include "vold.h"
33#include "blkdev.h"
34#include "diskmbr.h"
35
36#define DEBUG_BLKDEV 0
37
38static blkdev_list_t *list_root = NULL;
39
40static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
41 int minor, char *type, struct media *media);
42
43static int fat_valid_media(unsigned char media)
44{
45 return 0xf8 <= media || media == 0xf0;
46}
47
48char *blkdev_get_devpath(blkdev_t *blk)
49{
50 char *dp = malloc(256);
51 sprintf(dp, "%s/vold/%d:%d", DEVPATH, blk->major, blk->minor);
52 return dp;
53}
54
55int blkdev_refresh(blkdev_t *blk)
56{
57 int fd = 0;
58 char *devpath = NULL;
59 unsigned char *block = NULL;
60 int i, rc;
61
62 if (!(block = malloc(512)))
63 goto out;
64
65 /*
66 * Get the device size
67 */
68 devpath = blkdev_get_devpath(blk);
69
70 if ((fd = open(devpath, O_RDONLY)) < 0) {
71 LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno));
72 return -errno;
73 }
74
75 if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) {
76 LOGE("Unable to get device size (%m)");
77 return -errno;
78 }
79 close(fd);
80 free(devpath);
81
82 /*
83 * Open the disk partition table
84 */
85 devpath = blkdev_get_devpath(blk->disk);
86 if ((fd = open(devpath, O_RDONLY)) < 0) {
87 LOGE("Unable to open device '%s' (%s)", devpath,
88 strerror(errno));
89 free(devpath);
90 return -errno;
91 }
92
93 free(devpath);
94
95 if ((rc = read(fd, block, 512)) != 512) {
96 LOGE("Unable to read device partition table (%d, %s)",
97 rc, strerror(errno));
98 goto out;
99 }
100
101 /*
102 * If we're a disk, then process the partition table. Otherwise we're
103 * a partition so get the partition type
104 */
105
106 if (blk->type == blkdev_disk) {
107 blk->nr_parts = 0;
108
109 if ((block[0x1fe] != 0x55) || (block[0x1ff] != 0xAA)) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700110 LOGI("Disk %d:%d does not contain a partition table",
111 blk->major, blk->minor);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800112 goto out;
113 }
114
115 for (i = 0; i < 4; i++) {
116 struct dos_partition part;
117
118 dos_partition_dec(block + DOSPARTOFF + i * sizeof(struct dos_partition), &part);
119 if (part.dp_flag != 0 && part.dp_flag != 0x80) {
120 struct fat_boot_sector *fb = (struct fat_boot_sector *) &block[0];
121
122 if (!i && fb->reserved && fb->fats && fat_valid_media(fb->media)) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700123 LOGI("Detected FAT filesystem in partition table");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800124 break;
125 } else {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700126 LOGI("Partition table looks corrupt");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800127 break;
128 }
129 }
130 if (part.dp_size != 0 && part.dp_typ != 0)
131 blk->nr_parts++;
132 }
133 } else if (blk->type == blkdev_partition) {
134 struct dos_partition part;
135 int part_no = blk->minor -1;
136
The Android Open Source Projectf614d642009-03-18 17:39:49 -0700137 if (part_no < 4) {
138 dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part);
139 blk->part_type = part.dp_typ;
140 } else {
141 LOGW("Skipping partition %d", part_no);
142 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800143 }
144
145 out:
146
147 if (block)
148 free(block);
149
150 char tmp[255];
151 char tmp2[32];
152 sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)",
153 (blk->type == blkdev_disk ? "Disk" : "Partition"),
154 blk->major, blk->minor,
155 blk->nr_sec,
156 (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024);
157
158 if (blk->type == blkdev_disk)
159 sprintf(tmp2, " %d partitions", blk->nr_parts);
160 else
161 sprintf(tmp2, " type 0x%x", blk->part_type);
162
163 strcat(tmp, tmp2);
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700164 LOGI(tmp);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800165
166 close(fd);
167
168 return 0;
169}
170
171blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type)
172{
173 return _blkdev_create(disk, devpath, major, minor, type, media);
174}
175
176static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
177 int minor, char *type, struct media *media)
178{
179 blkdev_t *new;
180 struct blkdev_list *list_entry;
181
182 if (disk && disk->type != blkdev_disk) {
183 LOGE("Non disk parent specified for blkdev!");
184 return NULL;
185 }
186
187 if (!(new = malloc(sizeof(blkdev_t))))
188 return NULL;
189
190 memset(new, 0, sizeof(blkdev_t));
191
192 if (!(list_entry = malloc(sizeof(struct blkdev_list)))) {
193 free (new);
194 return NULL;
195 }
196 list_entry->dev = new;
197 list_entry->next = NULL;
198
199 if (!list_root)
200 list_root = list_entry;
201 else {
202 struct blkdev_list *list_scan = list_root;
203 while (list_scan->next)
204 list_scan = list_scan->next;
205 list_scan->next = list_entry;
206 }
207
208 if (devpath)
209 new->devpath = strdup(devpath);
210 new->major = major;
211 new->minor = minor;
212 new->media = media;
213 new->nr_sec = 0xffffffff;
214
215 if (disk)
216 new->disk = disk;
217 else
218 new->disk = new; // Note the self disk pointer
219
220 /* Create device nodes */
221 char nodepath[255];
222 mode_t mode = 0666 | S_IFBLK;
223 dev_t dev = (major << 8) | minor;
224
225 sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor);
226 if (mknod(nodepath, mode, dev) < 0) {
227 LOGE("Error making device nodes for '%s' (%s)",
228 nodepath, strerror(errno));
229 }
230
231 if (!strcmp(type, "disk"))
232 new->type = blkdev_disk;
233 else if (!strcmp(type, "partition"))
234 new->type = blkdev_partition;
235 else {
236 LOGE("Unknown block device type '%s'", type);
237 new->type = blkdev_unknown;
238 }
239
240 return new;
241}
242
243void blkdev_destroy(blkdev_t *blkdev)
244{
245 struct blkdev_list *list_next;
246
247 if (list_root->dev == blkdev) {
248 list_next = list_root->next;
249 free (list_root);
250 list_root = list_next;
251 } else {
252 struct blkdev_list *list_scan = list_root;
253 while (list_scan->next->dev != blkdev)
254 list_scan = list_scan -> next;
255 list_next = list_scan->next->next;
256 free(list_scan->next);
257 list_scan->next = list_next;
258 }
259
260 if (blkdev->devpath)
261 free(blkdev->devpath);
262
263 char nodepath[255];
264 sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor);
265 unlink(nodepath);
266
267 free(blkdev);
268}
269
270blkdev_t *blkdev_lookup_by_path(char *devpath)
271{
272 struct blkdev_list *list_scan = list_root;
273
274 while (list_scan) {
275 if (!strcmp(list_scan->dev->devpath, devpath))
276 return list_scan->dev;
277 list_scan = list_scan->next;
278 }
279 return NULL;
280}
281
282blkdev_t *blkdev_lookup_by_devno(int maj, int min)
283{
284 struct blkdev_list *list_scan = list_root;
285
286 while (list_scan) {
287 if ((list_scan->dev->major == maj) &&
288 (list_scan->dev->minor == min))
289 return list_scan->dev;
290 list_scan = list_scan->next;
291 }
292 return NULL;
293}
294
295/*
296 * Given a disk device, return the number of partitions which
297 * have yet to be processed.
298 */
299int blkdev_get_num_pending_partitions(blkdev_t *blk)
300{
301 struct blkdev_list *list_scan = list_root;
302 int num = blk->nr_parts;
303
304 if (blk->type != blkdev_disk)
305 return -EINVAL;
306
307 while (list_scan) {
308 if (list_scan->dev->type != blkdev_partition)
309 goto next;
310
311 if (list_scan->dev->major != blk->major)
312 goto next;
313
314 if (list_scan->dev->nr_sec != 0xffffffff &&
315 list_scan->dev->devpath) {
316 num--;
317 }
318 next:
319 list_scan = list_scan->next;
320 }
321 return num;
322}
323