/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdlib.h> #include <string.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mman.h> #include <linux/fs.h> #include <linux/msdos_fs.h> #include "vold.h" #include "blkdev.h" #include "diskmbr.h" #define DEBUG_BLKDEV 0 static blkdev_list_t *list_root = NULL; static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, char *type, struct media *media); static int fat_valid_media(unsigned char media) { return 0xf8 <= media || media == 0xf0; } char *blkdev_get_devpath(blkdev_t *blk) { char *dp = malloc(256); sprintf(dp, "%s/vold/%d:%d", DEVPATH, blk->major, blk->minor); return dp; } int blkdev_refresh(blkdev_t *blk) { int fd = 0; char *devpath = NULL; unsigned char *block = NULL; int i, rc; if (!(block = malloc(512))) goto out; /* * Get the device size */ devpath = blkdev_get_devpath(blk); if ((fd = open(devpath, O_RDONLY)) < 0) { LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno)); return -errno; } if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) { LOGE("Unable to get device size (%s)", strerror(errno)); return -errno; } close(fd); free(devpath); /* * Open the disk partition table */ devpath = blkdev_get_devpath(blk->disk); if ((fd = open(devpath, O_RDONLY)) < 0) { LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno)); free(devpath); return -errno; } free(devpath); if ((rc = read(fd, block, 512)) != 512) { LOGE("Unable to read device partition table (%d, %s)", rc, strerror(errno)); goto out; } /* * If we're a disk, then process the partition table. Otherwise we're * a partition so get the partition type */ if (blk->type == blkdev_disk) { blk->nr_parts = 0; if ((block[0x1fe] != 0x55) || (block[0x1ff] != 0xAA)) { LOGI("Disk %d:%d does not contain a partition table", blk->major, blk->minor); goto out; } for (i = 0; i < 4; i++) { struct dos_partition part; dos_partition_dec(block + DOSPARTOFF + i * sizeof(struct dos_partition), &part); if (part.dp_flag != 0 && part.dp_flag != 0x80) { struct fat_boot_sector *fb = (struct fat_boot_sector *) &block[0]; if (!i && fb->reserved && fb->fats && fat_valid_media(fb->media)) { LOGI("Detected FAT filesystem in partition table"); break; } else { LOGI("Partition table looks corrupt"); break; } } if (part.dp_size != 0 && part.dp_typ != 0) blk->nr_parts++; } } else if (blk->type == blkdev_partition) { struct dos_partition part; int part_no = blk->minor -1; if (part_no < 4) { dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part); blk->part_type = part.dp_typ; } else { LOGW("Skipping partition %d", part_no); } } out: if (block) free(block); char tmp[255]; char tmp2[32]; sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)", (blk->type == blkdev_disk ? "Disk" : "Partition"), blk->major, blk->minor, blk->nr_sec, (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024); if (blk->type == blkdev_disk) sprintf(tmp2, " %d partitions", blk->nr_parts); else sprintf(tmp2, " type 0x%x", blk->part_type); strcat(tmp, tmp2); LOGI(tmp); close(fd); return 0; } blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type) { return _blkdev_create(disk, devpath, major, minor, type, media); } static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, char *type, struct media *media) { blkdev_t *new; struct blkdev_list *list_entry; if (disk && disk->type != blkdev_disk) { LOGE("Non disk parent specified for blkdev!"); return NULL; } if (!(new = malloc(sizeof(blkdev_t)))) return NULL; memset(new, 0, sizeof(blkdev_t)); if (!(list_entry = malloc(sizeof(struct blkdev_list)))) { free (new); return NULL; } list_entry->dev = new; list_entry->next = NULL; if (!list_root) list_root = list_entry; else { struct blkdev_list *list_scan = list_root; while (list_scan->next) list_scan = list_scan->next; list_scan->next = list_entry; } if (devpath) new->devpath = strdup(devpath); new->major = major; new->minor = minor; new->media = media; new->nr_sec = 0xffffffff; if (disk) new->disk = disk; else new->disk = new; // Note the self disk pointer /* Create device nodes */ char nodepath[255]; mode_t mode = 0660 | S_IFBLK; dev_t dev = (major << 8) | minor; sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor); if (mknod(nodepath, mode, dev) < 0) { LOGE("Error making device nodes for '%s' (%s)", nodepath, strerror(errno)); } if (!strcmp(type, "disk")) new->type = blkdev_disk; else if (!strcmp(type, "partition")) new->type = blkdev_partition; else { LOGE("Unknown block device type '%s'", type); new->type = blkdev_unknown; } return new; } void blkdev_destroy(blkdev_t *blkdev) { struct blkdev_list *list_next; if (list_root->dev == blkdev) { list_next = list_root->next; free (list_root); list_root = list_next; } else { struct blkdev_list *list_scan = list_root; while (list_scan->next->dev != blkdev) list_scan = list_scan -> next; list_next = list_scan->next->next; free(list_scan->next); list_scan->next = list_next; } if (blkdev->devpath) free(blkdev->devpath); char nodepath[255]; sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor); unlink(nodepath); free(blkdev); } blkdev_t *blkdev_lookup_by_path(char *devpath) { struct blkdev_list *list_scan = list_root; while (list_scan) { if (!strcmp(list_scan->dev->devpath, devpath)) return list_scan->dev; list_scan = list_scan->next; } return NULL; } blkdev_t *blkdev_lookup_by_devno(int maj, int min) { struct blkdev_list *list_scan = list_root; while (list_scan) { if ((list_scan->dev->major == maj) && (list_scan->dev->minor == min)) return list_scan->dev; list_scan = list_scan->next; } return NULL; } /* * Given a disk device, return the number of partitions which * have yet to be processed. */ int blkdev_get_num_pending_partitions(blkdev_t *blk) { struct blkdev_list *list_scan = list_root; int num = blk->nr_parts; if (blk->type != blkdev_disk) return -EINVAL; while (list_scan) { if (list_scan->dev->type != blkdev_partition) goto next; if (list_scan->dev->major != blk->major) goto next; if (list_scan->dev->nr_sec != 0xffffffff && list_scan->dev->devpath) { num--; } next: list_scan = list_scan->next; } return num; }