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