blob: 533bc35f25be76d6a3cfc43f4400fa9932ded8d2 [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)) {
110 LOG_VOL("Disk %d:%d does not contain a partition table",
111 blk->major, blk->minor);
112 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)) {
123 LOG_VOL("Detected FAT filesystem in partition table");
124 break;
125 } else {
126 LOG_VOL("Partition table looks corrupt");
127 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
137 dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part);
138 blk->part_type = part.dp_typ;
139 }
140
141 out:
142
143 if (block)
144 free(block);
145
146 char tmp[255];
147 char tmp2[32];
148 sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)",
149 (blk->type == blkdev_disk ? "Disk" : "Partition"),
150 blk->major, blk->minor,
151 blk->nr_sec,
152 (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024);
153
154 if (blk->type == blkdev_disk)
155 sprintf(tmp2, " %d partitions", blk->nr_parts);
156 else
157 sprintf(tmp2, " type 0x%x", blk->part_type);
158
159 strcat(tmp, tmp2);
160 LOG_VOL(tmp);
161
162 close(fd);
163
164 return 0;
165}
166
167blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type)
168{
169 return _blkdev_create(disk, devpath, major, minor, type, media);
170}
171
172static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
173 int minor, char *type, struct media *media)
174{
175 blkdev_t *new;
176 struct blkdev_list *list_entry;
177
178 if (disk && disk->type != blkdev_disk) {
179 LOGE("Non disk parent specified for blkdev!");
180 return NULL;
181 }
182
183 if (!(new = malloc(sizeof(blkdev_t))))
184 return NULL;
185
186 memset(new, 0, sizeof(blkdev_t));
187
188 if (!(list_entry = malloc(sizeof(struct blkdev_list)))) {
189 free (new);
190 return NULL;
191 }
192 list_entry->dev = new;
193 list_entry->next = NULL;
194
195 if (!list_root)
196 list_root = list_entry;
197 else {
198 struct blkdev_list *list_scan = list_root;
199 while (list_scan->next)
200 list_scan = list_scan->next;
201 list_scan->next = list_entry;
202 }
203
204 if (devpath)
205 new->devpath = strdup(devpath);
206 new->major = major;
207 new->minor = minor;
208 new->media = media;
209 new->nr_sec = 0xffffffff;
210
211 if (disk)
212 new->disk = disk;
213 else
214 new->disk = new; // Note the self disk pointer
215
216 /* Create device nodes */
217 char nodepath[255];
218 mode_t mode = 0666 | S_IFBLK;
219 dev_t dev = (major << 8) | minor;
220
221 sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor);
222 if (mknod(nodepath, mode, dev) < 0) {
223 LOGE("Error making device nodes for '%s' (%s)",
224 nodepath, strerror(errno));
225 }
226
227 if (!strcmp(type, "disk"))
228 new->type = blkdev_disk;
229 else if (!strcmp(type, "partition"))
230 new->type = blkdev_partition;
231 else {
232 LOGE("Unknown block device type '%s'", type);
233 new->type = blkdev_unknown;
234 }
235
236 return new;
237}
238
239void blkdev_destroy(blkdev_t *blkdev)
240{
241 struct blkdev_list *list_next;
242
243 if (list_root->dev == blkdev) {
244 list_next = list_root->next;
245 free (list_root);
246 list_root = list_next;
247 } else {
248 struct blkdev_list *list_scan = list_root;
249 while (list_scan->next->dev != blkdev)
250 list_scan = list_scan -> next;
251 list_next = list_scan->next->next;
252 free(list_scan->next);
253 list_scan->next = list_next;
254 }
255
256 if (blkdev->devpath)
257 free(blkdev->devpath);
258
259 char nodepath[255];
260 sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor);
261 unlink(nodepath);
262
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 return NULL;
276}
277
278blkdev_t *blkdev_lookup_by_devno(int maj, int min)
279{
280 struct blkdev_list *list_scan = list_root;
281
282 while (list_scan) {
283 if ((list_scan->dev->major == maj) &&
284 (list_scan->dev->minor == min))
285 return list_scan->dev;
286 list_scan = list_scan->next;
287 }
288 return NULL;
289}
290
291/*
292 * Given a disk device, return the number of partitions which
293 * have yet to be processed.
294 */
295int blkdev_get_num_pending_partitions(blkdev_t *blk)
296{
297 struct blkdev_list *list_scan = list_root;
298 int num = blk->nr_parts;
299
300 if (blk->type != blkdev_disk)
301 return -EINVAL;
302
303 while (list_scan) {
304 if (list_scan->dev->type != blkdev_partition)
305 goto next;
306
307 if (list_scan->dev->major != blk->major)
308 goto next;
309
310 if (list_scan->dev->nr_sec != 0xffffffff &&
311 list_scan->dev->devpath) {
312 num--;
313 }
314 next:
315 list_scan = list_scan->next;
316 }
317 return num;
318}
319