diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
commit | dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0 (patch) | |
tree | 2ba8d1a0846d69b18f623515e8d9b5d9fe38b590 /vold | |
parent | e54eebbf1a908d65ee8cf80bab62821c05666d70 (diff) | |
download | system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.zip system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.gz system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'vold')
-rw-r--r-- | vold/Android.mk | 32 | ||||
-rw-r--r-- | vold/ProcessKiller.c | 222 | ||||
-rw-r--r-- | vold/blkdev.c | 319 | ||||
-rw-r--r-- | vold/blkdev.h | 64 | ||||
-rw-r--r-- | vold/cmd_dispatch.c | 115 | ||||
-rw-r--r-- | vold/cmd_dispatch.h | 31 | ||||
-rw-r--r-- | vold/devmapper.c | 463 | ||||
-rw-r--r-- | vold/devmapper.h | 70 | ||||
-rw-r--r-- | vold/diskmbr.h | 75 | ||||
-rwxr-xr-x | vold/format.c | 113 | ||||
-rw-r--r-- | vold/format.h | 26 | ||||
-rw-r--r-- | vold/geom_mbr_enc.c | 90 | ||||
-rw-r--r-- | vold/logwrapper.c | 147 | ||||
-rw-r--r-- | vold/logwrapper.h | 23 | ||||
-rw-r--r-- | vold/media.c | 165 | ||||
-rw-r--r-- | vold/media.h | 51 | ||||
-rw-r--r-- | vold/misc.c | 91 | ||||
-rw-r--r-- | vold/mmc.c | 293 | ||||
-rw-r--r-- | vold/mmc.h | 23 | ||||
-rw-r--r-- | vold/switch.c | 121 | ||||
-rw-r--r-- | vold/switch.h | 25 | ||||
-rw-r--r-- | vold/uevent.c | 443 | ||||
-rw-r--r-- | vold/uevent.h | 21 | ||||
-rw-r--r-- | vold/ums.c | 129 | ||||
-rw-r--r-- | vold/ums.h | 31 | ||||
-rw-r--r-- | vold/vold.c | 234 | ||||
-rw-r--r-- | vold/vold.h | 100 | ||||
-rw-r--r-- | vold/volmgr.c | 1229 | ||||
-rw-r--r-- | vold/volmgr.h | 135 | ||||
-rw-r--r-- | vold/volmgr_ext3.c | 184 | ||||
-rw-r--r-- | vold/volmgr_ext3.h | 27 | ||||
-rw-r--r-- | vold/volmgr_vfat.c | 135 | ||||
-rw-r--r-- | vold/volmgr_vfat.h | 29 |
33 files changed, 5256 insertions, 0 deletions
diff --git a/vold/Android.mk b/vold/Android.mk new file mode 100644 index 0000000..3dd9f87 --- /dev/null +++ b/vold/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + vold.c \ + cmd_dispatch.c \ + uevent.c \ + mmc.c \ + misc.c \ + blkdev.c \ + ums.c \ + geom_mbr_enc.c \ + volmgr.c \ + media.c \ + volmgr_vfat.c \ + volmgr_ext3.c \ + logwrapper.c \ + ProcessKiller.c\ + switch.c \ + format.c \ + devmapper.c + +LOCAL_MODULE:= vold + +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) + +LOCAL_CFLAGS := + +LOCAL_SHARED_LIBRARIES := libcutils + +include $(BUILD_EXECUTABLE) diff --git a/vold/ProcessKiller.c b/vold/ProcessKiller.c new file mode 100644 index 0000000..fc3eb37 --- /dev/null +++ b/vold/ProcessKiller.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +/* +** mountd process killer +*/ + +#include "vold.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> +#include <sys/stat.h> + + +static boolean ReadSymLink(const char* path, char* link) +{ + struct stat s; + int length; + + if (lstat(path, &s) < 0) + return false; + if ((s.st_mode & S_IFMT) != S_IFLNK) + return false; + + // we have a symlink + length = readlink(path, link, PATH_MAX - 1); + if (length <= 0) + return false; + link[length] = 0; + return true; +} + +static boolean PathMatchesMountPoint(const char* path, const char* mountPoint) +{ + int length = strlen(mountPoint); + if (length > 1 && strncmp(path, mountPoint, length) == 0) + { + // we need to do extra checking if mountPoint does not end in a '/' + if (mountPoint[length - 1] == '/') + return true; + // if mountPoint does not have a trailing slash, we need to make sure + // there is one in the path to avoid partial matches. + return (path[length] == 0 || path[length] == '/'); + } + + return false; +} + +static void GetProcessName(int pid, char buffer[PATH_MAX]) +{ + int fd; + sprintf(buffer, "/proc/%d/cmdline", pid); + fd = open(buffer, O_RDONLY); + if (fd < 0) { + strcpy(buffer, "???"); + } else { + int length = read(fd, buffer, PATH_MAX - 1); + buffer[length] = 0; + close(fd); + } +} + +static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint) +{ + DIR* dir; + struct dirent* de; + boolean fileOpen = false; + char path[PATH_MAX]; + char link[PATH_MAX]; + int parent_length; + + // compute path to process's directory of open files + sprintf(path, "/proc/%d/fd", pid); + dir = opendir(path); + if (!dir) + return false; + + // remember length of the path + parent_length = strlen(path); + // append a trailing '/' + path[parent_length++] = '/'; + + while ((de = readdir(dir)) != 0 && !fileOpen) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + // append the file name, after truncating to parent directory + path[parent_length] = 0; + strcat(path, de->d_name); + + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file %s", name, pid, link); + fileOpen = true; + } + } + + closedir(dir); + return fileOpen; +} + +static boolean CheckFileMaps(int pid, const char* mountPoint) +{ + FILE* file; + char buffer[PATH_MAX + 100]; + boolean mapOpen = false; + + sprintf(buffer, "/proc/%d/maps", pid); + file = fopen(buffer, "r"); + if (!file) + return false; + + while (!mapOpen && fgets(buffer, sizeof(buffer), file)) + { + // skip to the path + const char* path = strchr(buffer, '/'); + if (path && PathMatchesMountPoint(path, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file map for %s", name, pid, path); + mapOpen = true; + } + } + + fclose(file); + return mapOpen; +} + +static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message) +{ + char path[PATH_MAX]; + char link[PATH_MAX]; + + sprintf(path, "/proc/%d/%s", pid, name); + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has %s in %s", name, pid, message, mountPoint); + return true; + } + else + return false; +} + +static int get_pid(const char* s) +{ + int result = 0; + while (*s) { + if (!isdigit(*s)) return -1; + result = 10 * result + (*s++ - '0'); + } + return result; +} + +// hunt down and kill processes that have files open on the given mount point +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded) +{ + DIR* dir; + struct dirent* de; + + LOG_ERROR("KillProcessesWithOpenFiles %s", mountPoint); + dir = opendir("/proc"); + if (!dir) return; + + while ((de = readdir(dir)) != 0) + { + boolean killed = false; + // does the name look like a process ID? + int pid = get_pid(de->d_name); + if (pid == -1) continue; + + if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files + || CheckFileMaps(pid, mountPoint) // check for mmap() + || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory + || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot() + || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path + ) + { + int i; + boolean hit = false; + + for (i = 0; i < num_excluded; i++) { + if (pid == excluded[i]) { + LOG_ERROR("I just need a little more TIME captain!"); + hit = true; + break; + } + } + + if (!hit) { + LOG_ERROR("Killing process %d", pid); + kill(pid, (sigkill ? SIGKILL : SIGTERM)); + } + } + } + + closedir(dir); +} diff --git a/vold/blkdev.c b/vold/blkdev.c new file mode 100644 index 0000000..533bc35 --- /dev/null +++ b/vold/blkdev.c @@ -0,0 +1,319 @@ + +/* + * 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 (%m)"); + 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)) { + LOG_VOL("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)) { + LOG_VOL("Detected FAT filesystem in partition table"); + break; + } else { + LOG_VOL("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; + + dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part); + blk->part_type = part.dp_typ; + } + + 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); + LOG_VOL(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 = 0666 | 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; +} + diff --git a/vold/blkdev.h b/vold/blkdev.h new file mode 100644 index 0000000..e789739 --- /dev/null +++ b/vold/blkdev.h @@ -0,0 +1,64 @@ + +/* + * 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. + */ + +#ifndef _BLKDEV_H +#define _BLKDEV_H + +#include <sys/types.h> + +struct media; + +enum blk_type { blkdev_unknown, blkdev_disk, blkdev_partition }; + +struct blkdev { + char *devpath; + enum blk_type type; + struct media *media; + + // If type == blkdev_disk then nr_parts = number of partitions + int nr_parts; + + // If type == blkdev_partition then part_type = partition type + uint8_t part_type; + // If type == blkdev_partition + struct blkdev *disk; + + unsigned int nr_sec; + + int major; + int minor; +}; + +struct blkdev_list { + struct blkdev *dev; + struct blkdev_list *next; +}; + +typedef struct blkdev blkdev_t; +typedef struct blkdev_list blkdev_list_t; + +blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type); +blkdev_t *blkdev_create_pending_partition(blkdev_t *disk, char *dev_fspath, int major, int minor, struct media *media); +blkdev_t *blkdev_lookup_by_path(char *devpath); +blkdev_t *blkdev_lookup_by_devno(int maj, int min); +char *blkdev_get_devpath(blkdev_t *blk); + +void blkdev_destroy(blkdev_t *blk); + +int blkdev_get_num_pending_partitions(blkdev_t *blk); +int blkdev_refresh(blkdev_t *blk); +#endif diff --git a/vold/cmd_dispatch.c b/vold/cmd_dispatch.c new file mode 100644 index 0000000..ab65fd9 --- /dev/null +++ b/vold/cmd_dispatch.c @@ -0,0 +1,115 @@ + +/* + * 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 <unistd.h> +#include <errno.h> + +#include "vold.h" +#include "cmd_dispatch.h" +#include "ums.h" +#include "volmgr.h" + +struct cmd_dispatch { + char *cmd; + int (* dispatch) (char *); +}; + +static void dispatch_cmd(char *cmd); +static int do_send_ums_status(char *cmd); +static int do_set_ums_enable(char *cmd); +static int do_mount_volume(char *cmd); +static int do_eject_media(char *cmd); +static int do_format_media(char *cmd); + +static struct cmd_dispatch dispatch_table[] = { + { VOLD_CMD_ENABLE_UMS, do_set_ums_enable }, + { VOLD_CMD_DISABLE_UMS, do_set_ums_enable }, + { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status }, + { VOLD_CMD_MOUNT_VOLUME, do_mount_volume }, + { VOLD_CMD_EJECT_MEDIA, do_eject_media }, + { VOLD_CMD_FORMAT_MEDIA, do_format_media }, + { NULL, NULL } +}; + +int process_framework_command(int socket) +{ + int rc; + char buffer[101]; + + if ((rc = read(socket, buffer, sizeof(buffer) -1)) < 0) { + LOGE("Unable to read framework command (%s)", strerror(errno)); + return -errno; + } else if (!rc) + return -ECONNRESET; + + int start = 0; + int i; + + buffer[rc] = 0; + + for (i = 0; i < rc; i++) { + if (buffer[i] == 0) { + dispatch_cmd(buffer + start); + start = i + 1; + } + } + return 0; +} + +static void dispatch_cmd(char *cmd) +{ + struct cmd_dispatch *c; + + LOG_VOL("dispatch_cmd(%s):", cmd); + + for (c = dispatch_table; c->cmd != NULL; c++) { + if (!strncmp(c->cmd, cmd, strlen(c->cmd))) { + c->dispatch(cmd); + return; + } + } + + LOGE("No cmd handlers defined for '%s'", cmd); +} + +static int do_send_ums_status(char *cmd) +{ + return ums_send_status(); +} + +static int do_set_ums_enable(char *cmd) +{ + if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS)) + return volmgr_enable_ums(true); + + return volmgr_enable_ums(false); +} + +static int do_mount_volume(char *cmd) +{ + return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]); +} + +static int do_format_media(char *cmd) +{ + return volmgr_format_volume(&cmd[strlen("format_media:")]); +} + +static int do_eject_media(char *cmd) +{ + return volmgr_stop_volume_by_mountpoint(&cmd[strlen("eject_media:")]); +} diff --git a/vold/cmd_dispatch.h b/vold/cmd_dispatch.h new file mode 100644 index 0000000..f8ba6b3 --- /dev/null +++ b/vold/cmd_dispatch.h @@ -0,0 +1,31 @@ + +/* + * 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. + */ + +#ifndef _CMD_DISPATCH_H +#define _CMD_DISPATCH_H + +// These must match the strings in java/android/android/os/UsbListener.java +#define VOLD_CMD_ENABLE_UMS "enable_ums" +#define VOLD_CMD_DISABLE_UMS "disable_ums" +#define VOLD_CMD_SEND_UMS_STATUS "send_ums_status" + +// these commands should contain a volume mount point after the colon +#define VOLD_CMD_MOUNT_VOLUME "mount_volume:" +#define VOLD_CMD_EJECT_MEDIA "eject_media:" +#define VOLD_CMD_FORMAT_MEDIA "format_media:" + +#endif diff --git a/vold/devmapper.c b/vold/devmapper.c new file mode 100644 index 0000000..ef44c90 --- /dev/null +++ b/vold/devmapper.c @@ -0,0 +1,463 @@ + +/* + * 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 <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <sched.h> +#include <fcntl.h> + +#include <sys/mount.h> + +#include <linux/loop.h> +#include <linux/dm-ioctl.h> + +#include <cutils/config_utils.h> +#include <cutils/properties.h> + +#include "vold.h" +#include "devmapper.h" + +#define DEBUG_DEVMAPPER 1 + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static struct dm_ioctl *_dm_ioctl_setup(struct devmapping *dm, int flags) +{ + void *buffer; + void *p; + const size_t min_size = 16 * 1024; + size_t len = sizeof(struct dm_ioctl); + struct dm_ioctl *io; + struct dm_target_spec *tgt; + int i; + char params[1024]; + char key[80]; + + key[0] = '\0'; + for (i = 0; i < (int) sizeof(dm->key); i++) { + char tmp[8]; + + sprintf(tmp, "%02x", dm->key[i]); + strcat(key, tmp); + } + + char srcdev[128]; + + // XXX: Handle non crypt targets and non twofish (use param) + if (dm->src_type == dmsrc_loopback) + strcpy(srcdev, dm->type_data.loop.loop_dev); + else if (dm->src_type == dmsrc_partition) + strcpy(srcdev, dm->type_data.part.part_dev); + + sprintf(params, "twofish %s 0 %s 0", key, srcdev); + +LOG_VOL("Params = '%s'", params); + + if (len < min_size) + len = min_size; + + if (!(buffer = malloc(len))) { + LOGE("out of memory"); + return NULL; + } + + memset(buffer, 0, len); + io = buffer; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + io->data_size = len; + io->data_start = sizeof(struct dm_ioctl); + + io->flags = flags; + io->dev = 0; + + io->target_count = 1; + io->event_nr = 1; + strncpy(io->name, dm->target, sizeof(io->name)); + + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = (dm->size_mb * (1024 * 1024)) / 512; + strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type)); + + p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + strcpy((char *) p, params); + p+= strlen(params) + 1; + + p = _align(p, 8); + tgt->next = p - buffer; + + return io; +} + +static int get_next_available_dm() +{ + int i; + + for (i = 0; i < 8; i++) { + char path[255]; + sprintf(path, "/dev/block/dm-%d", i); + if ((access(path, F_OK) < 0) && (errno == ENOENT)) + return i; + } + + LOGE("Out of device mapper numbers"); + return -1; +} + +static int create_devmapping(struct devmapping *dm) +{ + struct dm_ioctl *io; + int rc, fd; + +#if DEBUG_DEVMAPPER + LOG_VOL("create_devmapping():"); +#endif + + if (dm->dm_no < 0) { + LOGE("Invalid dm_no set"); + return -EINVAL; + } + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening device mapper (%d)", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(dm, 0))) { + LOGE("Unable to setup ioctl (out of memory)"); + close(fd); + return -ENOMEM; + } + + if ((rc = ioctl(fd, DM_DEV_CREATE, io)) < 0) { + LOGE("device-mapper create ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(dm, DM_STATUS_TABLE_FLAG))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(fd, DM_TABLE_LOAD, io)) < 0) { + LOGE("device-mapper load ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(dm, 0))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(fd, DM_DEV_SUSPEND, io)) < 0) { + LOGE("device-mapper resume ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (fd); + return rc; +} + +static int loopback_start(struct devmapping *dm) +{ + int i; + int fd; + char filename[255]; + int rc; + +#if DEBUG_DEVMAPPER + LOG_VOL("loopback_start(%s):", dm->type_data.loop.loop_src); +#endif + + for (i = 0; i < MAX_LOOP; i++) { + struct loop_info info; + + sprintf(filename, "/dev/block/loop%d", i); + + if ((fd = open(filename, O_RDWR)) < 0) { + LOGE("Unable to open %s (%s)", filename, strerror(errno)); + return -errno; + } + + rc = ioctl(fd, LOOP_GET_STATUS, &info); + if (rc < 0 && errno == ENXIO) + break; + + close(fd); + + if (rc < 0) { + LOGE("Unable to get loop status for %s (%s)", filename, + strerror(errno)); + return -errno; + } + } + + if (i == MAX_LOOP) { + LOGE("Out of loop devices"); + return -ENOSPC; + } + + int file_fd; + + if ((file_fd = open(dm->type_data.loop.loop_src, O_RDWR)) < 0) { + LOGE("Unable to open %s (%s)", dm->type_data.loop.loop_src, + strerror(errno)); + return -errno; + } + + if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) { + LOGE("Error setting up loopback interface (%s)", strerror(errno)); + return -errno; + } + + dm->type_data.loop.loop_dev = strdup(filename); + dm->type_data.loop.loop_no = i; + + close(fd); + close(file_fd); + +#if DEBUG_DEVMAPPER + LOG_VOL("Loop setup on %s for %s", dm->type_data.loop.loop_dev, + dm->type_data.loop.loop_src); +#endif + + return 0; +} + +int devmapper_start(struct devmapping *dm) +{ + int rc; + char src_blkdev_path[255]; + +#if DEBUG_DEVMAPPER + LOG_VOL("devmapper_start()"); +#endif + + if (dm->src_type == dmsrc_loopback) { + if ((rc = loopback_start(dm)) < 0) + return rc; + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } else { + LOGE("devmapper_start(): Unsupported source type '%d'", dm->src_type); + return -ENOENT; + } + + /* + * Configure the device mapper + */ + if ((rc = create_devmapping(dm)) < 0) { + LOGE("Failed to create devmapping (%d)", rc); + // XXX: if loopback then tear down + return rc; + } + + return 0; +} + +struct devmapping *devmapper_init(char *src, char *src_type, uint32_t size_mb, + char *target, char *params, char *tgt_fs, char *mediapath) +{ + struct devmapping *dm; + + if (!(dm = malloc(sizeof(struct devmapping)))) { + LOGE("devmapper_init(): out of memory"); + return NULL; + } + + memset(dm, 0, sizeof(struct devmapping)); + + if (!strcmp(src_type, "loopback_file")) { + dm->src_type = dmsrc_loopback; + dm->type_data.loop.loop_src = strdup(src); + } else if (!strncmp(src_type, "partition ", strlen("partition "))) { + dm->src_type = dmsrc_partition; + char *p = strtok(src_type, " "); + if (!p) { + LOGE("Invalid partition specifier"); + goto out_free; + } + dm->type_data.part.part_type = strtoul(p, NULL, 0); + } else { + LOGE("Invalid src_type defined (%s)", src_type); + goto out_free; + } + + // XXX: Validate these + dm->size_mb = size_mb; + dm->target = strdup(target); + dm->params = strdup(params); + dm->tgt_fs = strdup(tgt_fs); + + if ((dm->dm_no = get_next_available_dm()) < 0) + goto out_free; + + sprintf(mediapath, "/devices/virtual/block/dm-%d", dm->dm_no); + + if (!(dm->media = media_create(mediapath, + "unknown", + "unknown", + media_devmapper))) { + LOGE("Unable to create media"); + goto out_free; + } + + return dm; + out_free: + if (dm->target) + free(dm->target); + if (dm->params) + free(dm->params); + if (dm->tgt_fs) + free(dm->tgt_fs); + + free(dm); + return NULL; +} + +int devmapper_genesis(struct devmapping *dm) +{ + + if (dm->src_type == dmsrc_loopback) { + int fd; + + LOG_VOL("devmapper_genesis(): Working on %s", + dm->type_data.loop.loop_src); + + unlink(dm->type_data.loop.loop_src); + + LOG_VOL("devmapper_genesis(): Creating imagefile (%u MB)", + dm->size_mb); + + if ((fd = creat(dm->type_data.loop.loop_src, 0600)) < 0) { + LOGE("Error creating imagefile (%s)", strerror(errno)); + return -errno; + } + + if (ftruncate(fd, (dm->size_mb * (1024 * 1024))) < 0) { + LOGE("Error truncating imagefile (%s)", strerror(errno)); + close(fd); + return -errno; + } + close(fd); + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } + + return devmapper_start(dm); +} + +static int destroy_devmapping(struct devmapping *dm) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + LOG_VOL("destroy_devmapping():"); + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening device mapper (%d)", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(dm, DM_PERSISTENT_DEV_FLAG))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) { + LOGE("device-mapper remove ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int loopback_stop(struct devmapping *dm) +{ + char devname[255]; + int device_fd; + int rc = 0; + + LOG_VOL("loopback_stop():"); + + device_fd = open(dm->type_data.loop.loop_dev, O_RDONLY); + if (device_fd < 0) { + LOG_ERROR("Failed to open loop (%d)", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("Failed to destroy loop (%d)", errno); + rc = -errno; + } + + close(device_fd); + return rc; +} + +int devmapper_stop(struct devmapping *dm) +{ + int rc; + + LOG_VOL("devmapper_stop():"); + + if ((rc = destroy_devmapping(dm))) + return rc; + + if (dm->src_type == dmsrc_loopback) { + if ((rc = loopback_stop(dm))) + return rc; + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } + return 0; +} diff --git a/vold/devmapper.h b/vold/devmapper.h new file mode 100644 index 0000000..3d8cab3 --- /dev/null +++ b/vold/devmapper.h @@ -0,0 +1,70 @@ + +/* + * 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. + */ + +#ifndef _DEVMAPPER_H +#define _DEVMAPPER_H + +#include <pthread.h> + +#include "vold.h" +#include "blkdev.h" +#include "media.h" + +#define MAX_LOOP 8 + +enum dm_src_type { + dmsrc_unknown, + dmsrc_loopback, + dmsrc_partition, +}; + +struct loop_data { + char *loop_src; + + char *loop_dev; + int loop_no; +}; + +struct part_data { + char part_type; + + char *part_dev; +}; + +struct devmapping { + enum dm_src_type src_type; + union { + struct loop_data loop; + struct part_data part; + } type_data; + + uint32_t size_mb; + char *target; + char *params; + char *tgt_fs; + + unsigned char key[16]; + int dm_no; + + media_t *media; +}; + +struct devmapping *devmapper_init(char *, char *, unsigned int, char *, char *, char *, char *); +int devmapper_start(struct devmapping *); +int devmapper_stop(struct devmapping *); +int devmapper_genesis(struct devmapping *); +#endif diff --git a/vold/diskmbr.h b/vold/diskmbr.h new file mode 100644 index 0000000..417e7ca --- /dev/null +++ b/vold/diskmbr.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)disklabel.h 8.2 (Berkeley) 7/10/94 + * $FreeBSD: src/sys/sys/diskmbr.h,v 1.99.2.1 2005/01/31 23:26:56 imp Exp $ + */ + +#ifndef _SYS_DISKMBR_H_ +#define _SYS_DISKMBR_H_ + +#define DOSBBSECTOR 0 /* DOS boot block relative sector number */ +#define DOSPARTOFF 446 +#define DOSPARTSIZE 16 +#define NDOSPART 4 +#define NEXTDOSPART 32 +#define DOSMAGICOFFSET 510 +#define DOSMAGIC 0xAA55 + +#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */ +#define DOSPTYP_LINSWP 0x82 /* Linux swap partition */ +#define DOSPTYP_LINUX 0x83 /* Linux partition */ +#define DOSPTYP_PMBR 0xee /* GPT Protective MBR */ +#define DOSPTYP_EXT 5 /* DOS extended partition */ +#define DOSPTYP_EXTLBA 15 /* DOS extended partition */ + +struct dos_partition { + unsigned char dp_flag; /* bootstrap flags */ + unsigned char dp_shd; /* starting head */ + unsigned char dp_ssect; /* starting sector */ + unsigned char dp_scyl; /* starting cylinder */ + unsigned char dp_typ; /* partition type */ + unsigned char dp_ehd; /* end head */ + unsigned char dp_esect; /* end sector */ + unsigned char dp_ecyl; /* end cylinder */ + u_int32_t dp_start; /* absolute starting sector number */ + u_int32_t dp_size; /* partition size in sectors */ +}; +#ifdef CTASSERT +CTASSERT(sizeof (struct dos_partition) == DOSPARTSIZE); +#endif + +void dos_partition_dec(void const *pp, struct dos_partition *d); +void dos_partition_enc(void *pp, struct dos_partition *d); + +#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */ +#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */ + +#define DIOCSMBR _IOW('M', 129, u_char[512]) + +#endif /* !_SYS_DISKMBR_H_ */ diff --git a/vold/format.c b/vold/format.c new file mode 100755 index 0000000..dd0515c --- /dev/null +++ b/vold/format.c @@ -0,0 +1,113 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include <linux/fs.h> + +#include "vold.h" +#include "blkdev.h" +#include "format.h" +#include "diskmbr.h" +#include "logwrapper.h" + +static char MKDOSFS_PATH[] = "/system/bin/mkdosfs"; +static char MKE2FS_PATH[] = "/system/bin/mke2fs"; + +int format_partition(blkdev_t *part, char *type) +{ + char *devpath; + int rc = -EINVAL; + + devpath = blkdev_get_devpath(part); + + if (!strcmp(type, FORMAT_TYPE_FAT32)) { + char *args[7]; + args[0] = MKDOSFS_PATH; + args[1] = "-F 32"; + args[2] = "-c 32"; + args[3] = "-n 2"; + args[4] = "-O android"; + args[5] = devpath; + args[6] = NULL; + rc = logwrap(6, args); + } else { + char *args[7]; + args[0] = MKE2FS_PATH; + args[1] = "-b 4096"; + args[2] = "-m 1"; + args[3] = "-L android"; + args[4] = "-v"; + args[5] = devpath; + args[6] = NULL; + rc = logwrap(6, args); + } + + free(devpath); + + if (rc == 0) { + LOG_VOL("Filesystem formatted OK"); + return 0; + } else { + LOGE("Format failed (unknokwn exit code %d)", rc); + return -EIO; + } + return 0; +} + +int initialize_mbr(blkdev_t *disk) +{ + int fd, rc; + unsigned char block[512]; + struct dos_partition part; + char *devpath; + + devpath = blkdev_get_devpath(disk); + + memset(&part, 0, sizeof(part)); + part.dp_flag = 0x80; + part.dp_typ = 0xc; + part.dp_start = ((1024 * 64) / 512) + 1; + part.dp_size = disk->nr_sec - part.dp_start; + + memset(block, 0, sizeof(block)); + block[0x1fe] = 0x55; + block[0x1ff] = 0xaa; + + dos_partition_enc(block + DOSPARTOFF, &part); + + if ((fd = open(devpath, O_RDWR)) < 0) { + LOGE("Error opening disk file (%s)", strerror(errno)); + return -errno; + } + free(devpath); + + if (write(fd, block, sizeof(block)) < 0) { + LOGE("Error writing MBR (%s)", strerror(errno)); + close(fd); + return -errno; + } + + if (ioctl(fd, BLKRRPART, NULL) < 0) { + LOGE("Error re-reading partition table (%s)", strerror(errno)); + close(fd); + return -errno; + } + close(fd); + return 0; +} diff --git a/vold/format.h b/vold/format.h new file mode 100644 index 0000000..73cc012 --- /dev/null +++ b/vold/format.h @@ -0,0 +1,26 @@ + +/* + * 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. + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define FORMAT_TYPE_EXT2 "ext2" +#define FORMAT_TYPE_FAT32 "fat32" + +int format_partition(blkdev_t *part, char *type); +int initialize_mbr(blkdev_t *disk); +#endif diff --git a/vold/geom_mbr_enc.c b/vold/geom_mbr_enc.c new file mode 100644 index 0000000..f1f8339 --- /dev/null +++ b/vold/geom_mbr_enc.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Functions to encode or decode struct dos_partition into a bytestream + * of correct endianess and packing. These functions do no validation + * or sanity checking, they only pack/unpack the fields correctly. + * + * NB! This file must be usable both in kernel and userland. + */ + +#include <sys/types.h> +#include <sys/endian.h> + +#include "diskmbr.h" + +static __inline uint32_t __unused +le32dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static __inline void +le32enc(void *pp, uint32_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +void +dos_partition_dec(void const *pp, struct dos_partition *d) +{ + unsigned char const *p = pp; + + d->dp_flag = p[0]; + d->dp_shd = p[1]; + d->dp_ssect = p[2]; + d->dp_scyl = p[3]; + d->dp_typ = p[4]; + d->dp_ehd = p[5]; + d->dp_esect = p[6]; + d->dp_ecyl = p[7]; + d->dp_start = le32dec(p + 8); + d->dp_size = le32dec(p + 12); +} + +void +dos_partition_enc(void *pp, struct dos_partition *d) +{ + unsigned char *p = pp; + + p[0] = d->dp_flag; + p[1] = d->dp_shd; + p[2] = d->dp_ssect; + p[3] = d->dp_scyl; + p[4] = d->dp_typ; + p[5] = d->dp_ehd; + p[6] = d->dp_esect; + p[7] = d->dp_ecyl; + le32enc(p + 8, d->dp_start); + le32enc(p + 12, d->dp_size); +} diff --git a/vold/logwrapper.c b/vold/logwrapper.c new file mode 100644 index 0000000..25d2281 --- /dev/null +++ b/vold/logwrapper.c @@ -0,0 +1,147 @@ +/* + * 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 <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include "private/android_filesystem_config.h" +#include "cutils/log.h" + +int parent(const char *tag, int parent_read) { + int status; + char buffer[4096]; + + int a = 0; // start index of unprocessed data + int b = 0; // end index of unprocessed data + int sz; + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { + + sz += b; + // Log one line at a time + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + buffer[b] = '\0'; + } else if (buffer[b] == '\n') { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + a = b + 1; + } + } + + if (a == 0 && b == sizeof(buffer) - 1) { + // buffer is full, flush + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + b = 0; + } else if (a != b) { + // Keep left-overs + b -= a; + memmove(buffer, &buffer[a], b); + a = 0; + } else { + a = 0; + b = 0; + } + + } + // Flush remaining data + if (a != b) { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + } + status = 0xAAAA; + if (wait(&status) != -1) { // Wait for child + if (WIFEXITED(status)) { + LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, + WEXITSTATUS(status)); + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, + WTERMSIG(status)); + else if (WIFSTOPPED(status)) + LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, + WSTOPSIG(status)); + } else + LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, + strerror(errno), errno); + return -EAGAIN; +} + +void child(int argc, char* argv[]) { + // create null terminated argv_child array + char* argv_child[argc + 1]; + memcpy(argv_child, argv, argc * sizeof(char *)); + argv_child[argc] = NULL; + + // XXX: PROTECT FROM VIKING KILLER + if (execvp(argv_child[0], argv_child)) { + LOG(LOG_ERROR, "logwrapper", + "executing %s failed: %s", argv_child[0], strerror(errno)); + exit(-1); + } +} + +int logwrap(int argc, char* argv[], pid_t *childPid) +{ + pid_t pid; + + int parent_ptty; + int child_ptty; + char *child_devname = NULL; + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty"); + return -errno; + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx"); + return -1; + } + + pid = fork(); + if (pid < 0) { + LOG(LOG_ERROR, "logwrapper", "Failed to fork"); + return -errno; + } else if (pid == 0) { + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Problem with child ptty"); + return -errno; + } + + // redirect stdout and stderr + close(parent_ptty); + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); + + child(argc, argv); + } else { + return parent(argv[0], parent_ptty); + } + + return 0; +} diff --git a/vold/logwrapper.h b/vold/logwrapper.h new file mode 100644 index 0000000..602e24c --- /dev/null +++ b/vold/logwrapper.h @@ -0,0 +1,23 @@ + +/* + * 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. + */ + +#ifndef _LOGWRAPPER_H +#define _LOGWRAPPER_H + +#include <stdlib.h> +int logwrap(int argc, char* argv[]); +#endif diff --git a/vold/media.c b/vold/media.c new file mode 100644 index 0000000..40637ff --- /dev/null +++ b/vold/media.c @@ -0,0 +1,165 @@ + +/* + * 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 <sys/types.h> + +#include "vold.h" +#include "media.h" + +static media_list_t *list_root = NULL; + +media_t *media_create(char *devpath, char *name, char *serial, media_type_t type) +{ + media_list_t *list_entry; + media_t *new; + + if (!(new = malloc(sizeof(media_t)))) + return NULL; + + memset(new, 0, sizeof(media_t)); + + if (!(list_entry = malloc(sizeof(media_list_t)))) { + free(new); + return NULL; + } + list_entry->media = new; + list_entry->next = NULL; + + if (!list_root) + list_root = list_entry; + else { + media_list_t *list_scan = list_root; + while(list_scan->next) + list_scan = list_scan->next; + list_scan->next = list_entry; + } + + new->devpath = strdup(devpath); + new->name = strdup(name); + if (!serial) + new->serial = 0; + else + new->serial = strtoul(serial, NULL, 0); + + new->media_type = type; + + return new; +} + +void media_destroy(media_t *media) +{ + media_list_t *list_next; + + if (list_root->media == media) { + list_next = list_root->next; + free(list_root); + list_root = list_next; + } else { + media_list_t *list_scan = list_root; + while (list_scan->next->media != media) + list_scan = list_scan -> next; + list_next = list_scan->next->next; + free(list_scan->next); + list_scan->next = list_next; + } + + free(media->devpath); + free(media->name); + + while(media->devs) + media_remove_blkdev(media, media->devs->dev); + free(media); +} + +media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match) +{ + media_list_t *list_scan = list_root; + + while (list_scan) { + if (fuzzy_match) { + if (!strncmp(list_scan->media->devpath, devpath, strlen(devpath))) + return list_scan->media; + } else { + if (!strcmp(list_scan->media->devpath, devpath)) + return list_scan->media; + } + list_scan = list_scan->next; + } +#if DEBUG_MEDIA + LOG_VOL("media_lookup_by_path(): No media found @ %s", devpath); +#endif + return NULL; +} + +int media_add_blkdev(media_t *card, blkdev_t *dev) +{ + blkdev_list_t *list_entry; + + if (!(list_entry = malloc(sizeof(blkdev_list_t)))) { + LOGE("Out of memory"); + return -ENOMEM; + } + + list_entry->next = NULL; + list_entry->dev = dev; + if (!card->devs) + card->devs = list_entry; + else { + blkdev_list_t *scan = card->devs; + + while(scan->next) + scan = scan->next; + + scan->next = list_entry; + } + return 0; +} + +void media_remove_blkdev(media_t *card, blkdev_t *dev) +{ + if (card->devs->dev == dev) + card->devs = card->devs->next; + else { + blkdev_list_t *scan = card->devs; + while (scan->next->dev != dev) + scan = scan -> next; + blkdev_list_t *next = scan->next->next; + free(scan->next); + scan->next = next; + } +} + +media_t *media_lookup_by_dev(blkdev_t *dev) +{ + media_list_t *media_scan = list_root; + + while (media_scan) { + blkdev_list_t *blk_scan = media_scan->media->devs; + while (blk_scan) { + if (blk_scan->dev == dev) + return media_scan->media; + blk_scan = blk_scan->next; + } + media_scan = media_scan->next; + } + return NULL; +} diff --git a/vold/media.h b/vold/media.h new file mode 100644 index 0000000..567ce04 --- /dev/null +++ b/vold/media.h @@ -0,0 +1,51 @@ + +/* + * 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. + */ + +#ifndef _MEDIA_H +#define _MEDIA_H + +#include <sys/types.h> + +#include "blkdev.h" + +typedef enum media_type { + media_unknown, + media_mmc, + media_devmapper, +} media_type_t; + +typedef struct media { + char *devpath; + char *name; + uint32_t serial; + media_type_t media_type; + + blkdev_list_t *devs; +} media_t; + +typedef struct media_list { + media_t *media; + struct media_list *next; +} media_list_t; + +media_t *media_create(char *devpath, char *name, char *serial, enum media_type); +media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match); +media_t *media_lookup_by_dev(blkdev_t *dev); +void media_destroy(media_t *media); +int media_add_blkdev(media_t *media, blkdev_t *dev); +void media_remove_blkdev(media_t *media, blkdev_t *dev); +#endif diff --git a/vold/misc.c b/vold/misc.c new file mode 100644 index 0000000..951414c --- /dev/null +++ b/vold/misc.c @@ -0,0 +1,91 @@ + +/* + * 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 <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +void *read_file(char *filename, ssize_t *_size) +{ + int ret, fd; + struct stat sb; + ssize_t size; + void *buffer = NULL; + + /* open the file */ + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + /* find out how big it is */ + if (fstat(fd, &sb) < 0) + goto bail; + size = sb.st_size; + + /* allocate memory for it to be read into */ + buffer = malloc(size); + if (!buffer) + goto bail; + + /* slurp it into our buffer */ + ret = read(fd, buffer, size); + if (ret != size) + goto bail; + + /* let the caller know how big it is */ + *_size = size; + +bail: + close(fd); + return buffer; +} +char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer) +{ + int i; + + strcpy(buffer, path); + + for (i = 0; i < num_elements_to_remove; i++) { + char *p = &buffer[strlen(buffer)-1]; + + for (p = &buffer[strlen(buffer) -1]; *p != '/'; p--); + *p = '\0'; + } + + return buffer; +} + +char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var) +{ + char filename[255]; + char *p; + ssize_t sz; + + sprintf(filename, "/sys%s/%s", devpath, var); + p = read_file(filename, &sz); + p[(strlen(p) - 1)] = '\0'; + strncpy(buffer, p, maxlen); + free(p); + return buffer; +} + diff --git a/vold/mmc.c b/vold/mmc.c new file mode 100644 index 0000000..0f08964 --- /dev/null +++ b/vold/mmc.c @@ -0,0 +1,293 @@ + +/* + * 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 <sys/types.h> + +#include "vold.h" +#include "mmc.h" +#include "media.h" + +#define DEBUG_BOOTSTRAP 0 + +static int mmc_bootstrap_controller(char *sysfs_path); +static int mmc_bootstrap_card(char *sysfs_path); +static int mmc_bootstrap_block(char *devpath); +static int mmc_bootstrap_mmcblk(char *devpath); +static int mmc_bootstrap_mmcblk_partition(char *devpath); + +/* + * Bootstrap our mmc information. + */ +int mmc_bootstrap() +{ + DIR *d; + struct dirent *de; + + if (!(d = opendir(SYSFS_CLASS_MMC_PATH))) { + LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_MMC_PATH); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + sprintf(tmp, "%s/%s", SYSFS_CLASS_MMC_PATH, de->d_name); + if (mmc_bootstrap_controller(tmp)) + LOG_ERROR("Error bootstrapping controller '%s' (%m)", tmp); + } + + closedir(d); + + return 0; +} + +static int mmc_bootstrap_controller(char *sysfs_path) +{ + DIR *d; + struct dirent *de; + +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_controller(%s):", sysfs_path); +#endif + if (!(d = opendir(sysfs_path))) { + LOG_ERROR("Unable to open '%s' (%m)", sysfs_path); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + if ((!strcmp(de->d_name, "uevent")) || + (!strcmp(de->d_name, "subsystem")) || + (!strcmp(de->d_name, "device")) || + (!strcmp(de->d_name, "power"))) { + continue; + } + + sprintf(tmp, "%s/%s", sysfs_path, de->d_name); + + if (mmc_bootstrap_card(tmp) < 0) + LOG_ERROR("Error bootstrapping card '%s' (%m)", tmp); + } // while + + closedir(d); + return 0; +} + +static int mmc_bootstrap_card(char *sysfs_path) +{ + char saved_cwd[255]; + char new_cwd[255]; + char *devpath; + char *uevent_params[4]; + char *p; + char filename[255]; + char tmp[255]; + ssize_t sz; + +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_card(%s):", sysfs_path); +#endif + + /* + * sysfs_path is based on /sys/class, but we want the actual device class + */ + if (!getcwd(saved_cwd, sizeof(saved_cwd))) { + LOGE("Error getting working dir path"); + return -errno; + } + + if (chdir(sysfs_path) < 0) { + LOGE("Unable to chdir to %s (%m)", sysfs_path); + return -errno; + } + + if (!getcwd(new_cwd, sizeof(new_cwd))) { + LOGE("Buffer too small for device path"); + return -errno; + } + + if (chdir(saved_cwd) < 0) { + LOGE("Unable to restore working dir"); + return -errno; + } + + devpath = &new_cwd[4]; // Skip over '/sys' + + /* + * Collect parameters so we can simulate a UEVENT + */ + sprintf(tmp, "DEVPATH=%s", devpath); + uevent_params[0] = (char *) strdup(tmp); + + sprintf(filename, "/sys%s/type", devpath); + p = read_file(filename, &sz); + p[strlen(p) - 1] = '\0'; + sprintf(tmp, "MMC_TYPE=%s", p); + free(p); + uevent_params[1] = (char *) strdup(tmp); + + sprintf(filename, "/sys%s/name", devpath); + p = read_file(filename, &sz); + p[strlen(p) - 1] = '\0'; + sprintf(tmp, "MMC_NAME=%s", p); + free(p); + uevent_params[2] = (char *) strdup(tmp); + + uevent_params[3] = (char *) NULL; + + if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%m)"); + return -errno; + } + + /* + * Check for block drivers + */ + char block_devpath[255]; + sprintf(tmp, "%s/block", devpath); + sprintf(filename, "/sys%s/block", devpath); + if (!access(filename, F_OK)) { + if (mmc_bootstrap_block(tmp)) { + LOGE("Error bootstrapping block @ %s", tmp); + } + } + + return 0; +} + +static int mmc_bootstrap_block(char *devpath) +{ + char blockdir_path[255]; + DIR *d; + struct dirent *de; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_block(%s):", devpath); +#endif + + sprintf(blockdir_path, "/sys%s", devpath); + + if (!(d = opendir(blockdir_path))) { + LOGE("Failed to opendir %s", devpath); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + sprintf(tmp, "%s/%s", devpath, de->d_name); + if (mmc_bootstrap_mmcblk(tmp)) + LOGE("Error bootstraping mmcblk @ %s", tmp); + } + closedir(d); + return 0; +} + +static int mmc_bootstrap_mmcblk(char *devpath) +{ + char *mmcblk_devname; + int part_no; + int rc; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_mmcblk(%s):", devpath); +#endif + + if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) { + LOGE("Error bootstrapping mmcblk partition '%s'", devpath); + return rc; + } + + for (mmcblk_devname = &devpath[strlen(devpath)]; + *mmcblk_devname != '/'; mmcblk_devname--); + mmcblk_devname++; + + for (part_no = 0; part_no < 4; part_no++) { + char part_file[255]; + sprintf(part_file, "/sys%s/%sp%d", devpath, mmcblk_devname, part_no); + if (!access(part_file, F_OK)) { + char part_devpath[255]; + + sprintf(part_devpath, "%s/%sp%d", devpath, mmcblk_devname, part_no); + if (mmc_bootstrap_mmcblk_partition(part_devpath)) + LOGE("Error bootstrapping mmcblk partition '%s'", part_devpath); + } + } + + return 0; +} + +static int mmc_bootstrap_mmcblk_partition(char *devpath) +{ + char filename[255]; + char *uevent_buffer; + ssize_t sz; + char *uevent_params[4]; + char tmp[255]; + FILE *fp; + char line[255]; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_mmcblk_partition(%s):", devpath); +#endif + + sprintf(tmp, "DEVPATH=%s", devpath); + uevent_params[0] = strdup(tmp); + + sprintf(filename, "/sys%s/uevent", devpath); + if (!(fp = fopen(filename, "r"))) { + LOGE("Unable to open '%s' (%m)", filename); + return -errno; + } + + while (fgets(line, sizeof(line), fp)) { + line[strlen(line)-1] = 0; + if (!strncmp(line, "DEVTYPE=", 8)) + uevent_params[1] = strdup(line); + else if (!strncmp(line, "MAJOR=",6)) + uevent_params[2] = strdup(line); + else if (!strncmp(line, "MINOR=",6)) + uevent_params[3] = strdup(line); + } + fclose(fp); + + if (!uevent_params[1] || !uevent_params[2] || !uevent_params[3]) { + LOGE("mmcblk uevent missing required params"); + return -1; + } + uevent_params[4] = '\0'; + + if (simulate_uevent("block", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%m)"); + return -errno; + } + return 0; +} diff --git a/vold/mmc.h b/vold/mmc.h new file mode 100644 index 0000000..5a5d184 --- /dev/null +++ b/vold/mmc.h @@ -0,0 +1,23 @@ + +/* + * 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. + */ + +#ifndef _MMC_H +#define _MMC_H + +#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host" + +#endif diff --git a/vold/switch.c b/vold/switch.c new file mode 100644 index 0000000..ba9ddb3 --- /dev/null +++ b/vold/switch.c @@ -0,0 +1,121 @@ + +/* + * 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 <sys/types.h> + +#include "vold.h" +#include "switch.h" + +#define DEBUG_BOOTSTRAP 0 + +static int mmc_bootstrap_switch(char *sysfs_path); + +int switch_bootstrap() +{ + DIR *d; + struct dirent *de; + + if (!(d = opendir(SYSFS_CLASS_SWITCH_PATH))) { + LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_SWITCH_PATH); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + sprintf(tmp, "%s/%s", SYSFS_CLASS_SWITCH_PATH, de->d_name); + if (mmc_bootstrap_switch(tmp)) + LOG_ERROR("Error bootstrapping switch '%s' (%m)", tmp); + } + + closedir(d); + + return 0; +} + +static int mmc_bootstrap_switch(char *sysfs_path) +{ +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_switch(%s):", sysfs_path); +#endif + + char filename[255]; + char name[255]; + char state[255]; + char tmp[255]; + char *uevent_params[3]; + char devpath[255]; + FILE *fp; + + /* + * Read switch name + */ + sprintf(filename, "%s/name", sysfs_path); + if (!(fp = fopen(filename, "r"))) { + LOGE("Error opening switch name path '%s' (%s)", + sysfs_path, strerror(errno)); + return -errno; + } + if (!fgets(name, sizeof(name), fp)) { + LOGE("Unable to read switch name"); + fclose(fp); + return -EIO; + } + fclose(fp); + + name[strlen(name) -1] = '\0'; + sprintf(devpath, "/devices/virtual/switch/%s", name); + sprintf(tmp, "SWITCH_NAME=%s", name); + uevent_params[0] = (char *) strdup(tmp); + + /* + * Read switch state + */ + sprintf(filename, "%s/state", sysfs_path); + if (!(fp = fopen(filename, "r"))) { + LOGE("Error opening switch state path '%s' (%s)", + sysfs_path, strerror(errno)); + return -errno; + } + if (!fgets(state, sizeof(state), fp)) { + LOGE("Unable to read switch state"); + fclose(fp); + return -EIO; + } + fclose(fp); + + state[strlen(state) -1] = '\0'; + sprintf(tmp, "SWITCH_STATE=%s", state); + uevent_params[1] = (char *) strdup(tmp); + + uevent_params[2] = (char *) NULL; + + if (simulate_uevent("switch", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%s)", strerror(errno)); + return -errno; + } + + return 0; +} diff --git a/vold/switch.h b/vold/switch.h new file mode 100644 index 0000000..6729f2d --- /dev/null +++ b/vold/switch.h @@ -0,0 +1,25 @@ + +/* + * 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. + */ + +#ifndef _SWITCH_H +#define _SWITCH_H + +#include "vold.h" + +#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch" + +#endif diff --git a/vold/uevent.c b/vold/uevent.c new file mode 100644 index 0000000..b1a6944 --- /dev/null +++ b/vold/uevent.c @@ -0,0 +1,443 @@ + +/* + * 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 <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include "vold.h" +#include "uevent.h" +#include "mmc.h" +#include "blkdev.h" +#include "volmgr.h" +#include "media.h" + +#define DEBUG_UEVENT 0 + +#define UEVENT_PARAMS_MAX 32 + +enum uevent_action { action_add, action_remove, action_change }; + +struct uevent { + char *path; + enum uevent_action action; + char *subsystem; + char *param[UEVENT_PARAMS_MAX]; + unsigned int seqnum; +}; + +struct uevent_dispatch { + char *subsystem; + int (* dispatch) (struct uevent *); +}; + +static void dump_uevent(struct uevent *); +static int dispatch_uevent(struct uevent *event); +static void free_uevent(struct uevent *event); +static char *get_uevent_param(struct uevent *event, char *param_name); + +static int handle_powersupply_event(struct uevent *event); +static int handle_switch_event(struct uevent *); +static int handle_battery_event(struct uevent *); +static int handle_mmc_event(struct uevent *); +static int handle_block_event(struct uevent *); +static int handle_bdi_event(struct uevent *); +static void _cb_blkdev_ok_to_destroy(blkdev_t *dev); + +static struct uevent_dispatch dispatch_table[] = { + { "switch", handle_switch_event }, + { "battery", handle_battery_event }, + { "mmc", handle_mmc_event }, + { "block", handle_block_event }, + { "bdi", handle_bdi_event }, + { "power_supply", handle_powersupply_event }, + { NULL, NULL } +}; + +static boolean low_batt = false; +static boolean door_open = true; + +int process_uevent_message(int socket) +{ + char buffer[64 * 1024]; // Thank god we're not in the kernel :) + int count; + char *s = buffer; + char *end; + struct uevent *event; + int param_idx = 0; + int i; + int first = 1; + int rc = 0; + + if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) { + LOGE("Error receiving uevent (%s)", strerror(errno)); + return -errno; + } + + if (!(event = malloc(sizeof(struct uevent)))) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -errno; + } + + memset(event, 0, sizeof(struct uevent)); + + end = s + count; + while (s < end) { + if (first) { + char *p; + for (p = s; *p != '@'; p++); + p++; + event->path = strdup(p); + first = 0; + } else { + if (!strncmp(s, "ACTION=", strlen("ACTION="))) { + char *a = s + strlen("ACTION="); + + if (!strcmp(a, "add")) + event->action = action_add; + else if (!strcmp(a, "change")) + event->action = action_change; + else if (!strcmp(a, "remove")) + event->action = action_remove; + } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) + event->seqnum = atoi(s + strlen("SEQNUM=")); + else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) + event->subsystem = strdup(s + strlen("SUBSYSTEM=")); + else + event->param[param_idx++] = strdup(s); + } + s+= strlen(s) + 1; + } + + rc = dispatch_uevent(event); + + free_uevent(event); + return rc; +} + +int simulate_uevent(char *subsys, char *path, char *action, char **params) +{ + struct uevent *event; + char tmp[255]; + int i, rc; + + if (!(event = malloc(sizeof(struct uevent)))) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -errno; + } + + memset(event, 0, sizeof(struct uevent)); + + event->subsystem = strdup(subsys); + + if (!strcmp(action, "add")) + event->action = action_add; + else if (!strcmp(action, "change")) + event->action = action_change; + else if (!strcmp(action, "remove")) + event->action = action_remove; + else { + LOGE("Invalid action '%s'", action); + return -1; + } + + event->path = strdup(path); + + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!params[i]) + break; + event->param[i] = strdup(params[i]); + } + + rc = dispatch_uevent(event); + free_uevent(event); + return rc; +} + +static int dispatch_uevent(struct uevent *event) +{ + int i; + +#if DEBUG_UEVENT + dump_uevent(event); +#endif + for (i = 0; dispatch_table[i].subsystem != NULL; i++) { + if (!strcmp(dispatch_table[i].subsystem, event->subsystem)) + return dispatch_table[i].dispatch(event); + } + +#if DEBUG_UEVENT + LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem); +#endif + return 0; +} + +static void dump_uevent(struct uevent *event) +{ + int i; + + LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s", + event->seqnum, event->subsystem, event->action, event->path); + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + LOG_VOL("%s", event->param[i]); + } +} + +static void free_uevent(struct uevent *event) +{ + int i; + free(event->path); + free(event->subsystem); + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + free(event->param[i]); + } + free(event); +} + +static char *get_uevent_param(struct uevent *event, char *param_name) +{ + int i; + + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + if (!strncmp(event->param[i], param_name, strlen(param_name))) + return &event->param[i][strlen(param_name) + 1]; + } + + LOGE("get_uevent_param(): No parameter '%s' found", param_name); + return NULL; +} + +/* + * --------------- + * Uevent Handlers + * --------------- + */ + +static int handle_powersupply_event(struct uevent *event) +{ + char *ps_type = get_uevent_param(event, "POWER_SUPPLY_TYPE"); + char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY"); + + if (!strcasecmp(ps_type, "battery")) { + int capacity = atoi(ps_cap); + + if (capacity < 5) + low_batt = true; + else + low_batt = false; +LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open); + volmgr_safe_mode(low_batt || door_open); + } + return 0; +} + +static int handle_switch_event(struct uevent *event) +{ + char *name = get_uevent_param(event, "SWITCH_NAME"); + char *state = get_uevent_param(event, "SWITCH_STATE"); + + + if (!strcmp(name, "usb_mass_storage")) { + if (!strcmp(state, "online")) { + ums_hostconnected_set(true); + } else { + ums_hostconnected_set(false); + volmgr_enable_ums(false); + } + } else if (!strcmp(name, "sd-door")) { + if (!strcmp(state, "open")) + door_open = true; + else + door_open = false; +LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open); + volmgr_safe_mode(low_batt || door_open); + } else + LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name); + + return 0; +} + +static int handle_battery_event(struct uevent *event) +{ + return 0; +} + +static int handle_block_event(struct uevent *event) +{ + char mediapath[255]; + media_t *media; + int n; + int maj, min; + blkdev_t *blkdev; + + /* + * Look for backing media for this block device + */ + if (!strncmp(get_uevent_param(event, "DEVPATH"), + "/devices/virtual/", + strlen("/devices/virtual/"))) { + n = 0; + } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk")) + n = 2; + else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition")) + n = 3; + else { + LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE")); + return -EINVAL; + } + + truncate_sysfs_path(event->path, n, mediapath); + + if (!(media = media_lookup_by_path(mediapath, false))) { +#if DEBUG_UEVENT + LOG_VOL("No backend media found @ device path '%s'", mediapath); +#endif + return 0; + } + + maj = atoi(get_uevent_param(event, "MAJOR")); + min = atoi(get_uevent_param(event, "MINOR")); + + if (event->action == action_add) { + blkdev_t *disk; + + /* + * If there isn't a disk already its because *we* + * are the disk + */ + disk = blkdev_lookup_by_devno(maj, 0); + + if (!(blkdev = blkdev_create(disk, + event->path, + maj, + min, + media, + get_uevent_param(event, "DEVTYPE")))) { + LOGE("Unable to allocate new blkdev (%m)"); + return -1; + } + + blkdev_refresh(blkdev); + + /* + * Add the blkdev to media + */ + int rc; + if ((rc = media_add_blkdev(media, blkdev)) < 0) { + LOGE("Unable to add blkdev to card (%d)", rc); + return rc; + } + + LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d", + blkdev->major, blkdev->minor, media->name, mediapath, + blkdev_get_num_pending_partitions(blkdev->disk)); + + if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) { + if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) { + LOGE("Volmgr failed to handle device (%d)", rc); + return rc; + } + } + } else if (event->action == action_remove) { + if (!(blkdev = blkdev_lookup_by_devno(maj, min))) + return 0; + + LOG_VOL("Destroying blkdev %d.%d @ %s on media %s", blkdev->major, + blkdev->minor, blkdev->devpath, media->name); + volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy); + + } else if (event->action == action_change) { + if (!(blkdev = blkdev_lookup_by_devno(maj, min))) + return 0; + + LOG_VOL("Modified blkdev %d.%d @ %s on media %s", blkdev->major, + blkdev->minor, blkdev->devpath, media->name); + + blkdev_refresh(blkdev); + } else { +#if DEBUG_UEVENT + LOG_VOL("No handler implemented for action %d", event->action); +#endif + } + return 0; +} + +static void _cb_blkdev_ok_to_destroy(blkdev_t *dev) +{ + media_t *media = media_lookup_by_dev(dev); + if (media) + media_remove_blkdev(media, dev); + blkdev_destroy(dev); +} + +static int handle_bdi_event(struct uevent *event) +{ + return 0; +} + +static int handle_mmc_event(struct uevent *event) +{ + if (event->action == action_add) { + media_t *media; + char serial[80]; + char *type; + + /* + * Pull card information from sysfs + */ + type = get_uevent_param(event, "MMC_TYPE"); + if (strcmp(type, "SD") && strcmp(type, "MMC")) + return 0; + + read_sysfs_var(serial, sizeof(serial), event->path, "serial"); + if (!(media = media_create(event->path, + get_uevent_param(event, "MMC_NAME"), + serial, + media_mmc))) { + LOGE("Unable to allocate new media (%m)"); + return -1; + } + LOG_VOL("New MMC card '%s' (serial %u) added @ %s", media->name, + media->serial, media->devpath); + } else if (event->action == action_remove) { + media_t *media; + + if (!(media = media_lookup_by_path(event->path, false))) { + LOGE("Unable to lookup media '%s'", event->path); + return -1; + } + + LOG_VOL("MMC card '%s' (serial %u) @ %s removed", media->name, + media->serial, media->devpath); + media_destroy(media); + } else { +#if DEBUG_UEVENT + LOG_VOL("No handler implemented for action %d", event->action); +#endif + } + + return 0; +} diff --git a/vold/uevent.h b/vold/uevent.h new file mode 100644 index 0000000..0e9e671 --- /dev/null +++ b/vold/uevent.h @@ -0,0 +1,21 @@ + +/* + * 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. + */ + +#ifndef _UEVENT_MSG_H +#define _UEVENT_MSG_H + +#endif diff --git a/vold/ums.c b/vold/ums.c new file mode 100644 index 0000000..4d0fc25 --- /dev/null +++ b/vold/ums.c @@ -0,0 +1,129 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include "vold.h" +#include "ums.h" + +#define DEBUG_UMS 0 + +static boolean host_connected = false; +static boolean ums_enabled = false; + +int ums_bootstrap(void) +{ + return 0; +} + +void ums_enabled_set(boolean enabled) +{ + ums_enabled = enabled; + send_msg(enabled ? VOLD_EVT_UMS_ENABLED : VOLD_EVT_UMS_DISABLED); +} + +boolean ums_enabled_get() +{ + return ums_enabled; +} + +void ums_hostconnected_set(boolean connected) +{ +#if DEBUG_UMS + LOG_VOL("ums_hostconnected_set(%d):", connected); +#endif + host_connected = connected; + + if (!connected) + ums_enabled_set(false); + send_msg(connected ? VOLD_EVT_UMS_CONNECTED : VOLD_EVT_UMS_DISCONNECTED); +} + +int ums_enable(char *dev_fspath, char *lun_syspath) +{ + LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath); + + int fd; + char filename[255]; + + sprintf(filename, "/sys/%s/file", lun_syspath); + if ((fd = open(filename, O_WRONLY)) < 0) { + LOGE("Unable to open '%s' (%s)", filename, strerror(errno)); + return -errno; + } + + if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) { + LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); + close(fd); + return -errno; + } + + close(fd); + return 0; +} + +int ums_disable(char *lun_syspath) +{ +#if DEBUG_UMS + LOG_VOL("ums_disable(%s):", lun_syspath); +#endif + + int fd; + char filename[255]; + + sprintf(filename, "/sys/%s/file", lun_syspath); + if ((fd = open(filename, O_WRONLY)) < 0) { + LOGE("Unable to open '%s' (%s)", filename, strerror(errno)); + return -errno; + } + + char ch = 0; + + if (write(fd, &ch, 1) < 0) { + LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); + close(fd); + return -errno; + } + + close(fd); + return 0; +} + +boolean ums_hostconnected_get(void) +{ + return host_connected; +} + +int ums_send_status(void) +{ + int rc; + +#if DEBUG_UMS + LOG_VOL("ums_send_status():"); +#endif + + rc = send_msg(ums_enabled_get() ? VOLD_EVT_UMS_ENABLED : + VOLD_EVT_UMS_DISABLED); + if (rc < 0) + return rc; + + rc = send_msg(ums_hostconnected_get() ? VOLD_EVT_UMS_CONNECTED : + VOLD_EVT_UMS_DISCONNECTED); + + return rc; +} diff --git a/vold/ums.h b/vold/ums.h new file mode 100644 index 0000000..02cdec3 --- /dev/null +++ b/vold/ums.h @@ -0,0 +1,31 @@ + +/* + * 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. + */ + +#ifndef _UMS_H +#define _UMS_H + +// these must match the corresponding strings in java/android/android/os/UsbListener.java +#define VOLD_EVT_UMS_ENABLED "ums_enabled" +#define VOLD_EVT_UMS_DISABLED "ums_disabled" +#define VOLD_EVT_UMS_CONNECTED "ums_connected" +#define VOLD_EVT_UMS_DISCONNECTED "ums_disconnected" + + +int ums_send_status(void); +int ums_enable(char *device_file, char *lun_syspath); +int ums_disable(char *lun_syspath); +#endif diff --git a/vold/vold.c b/vold/vold.c new file mode 100644 index 0000000..17331ac --- /dev/null +++ b/vold/vold.c @@ -0,0 +1,234 @@ + +/* + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <pthread.h> + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <cutils/config_utils.h> +#include <cutils/cpu_info.h> +#include <cutils/properties.h> +#include <cutils/sockets.h> + +#include <linux/netlink.h> + +#include <private/android_filesystem_config.h> + +#include "vold.h" +#include "volmgr.h" + + +#define VOLD_SOCKET "vold" + +/* + * Globals + */ + +static int ver_major = 2; +static int ver_minor = 0; +static pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER; +static int fw_sock = -1; + +int main(int argc, char **argv) +{ + int door_sock = -1; + int uevent_sock = -1; + struct sockaddr_nl nladdr; + int uevent_sz = 64 * 1024; + + LOG_VOL("Android Volume Daemon version %d.%d", ver_major, ver_minor); + + /* + * Create all the various sockets we'll need + */ + + // Socket to listen on for incomming framework connections + if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) { + LOGE("Obtaining file descriptor socket '%s' failed: %s", + VOLD_SOCKET, strerror(errno)); + exit(1); + } + + if (listen(door_sock, 4) < 0) { + LOGE("Unable to listen on fd '%d' for socket '%s': %s", + door_sock, VOLD_SOCKET, strerror(errno)); + exit(1); + } + + mkdir("/dev/block/vold", 0755); + + // Socket to listen on for uevent changes + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + nladdr.nl_groups = 0xffffffff; + + if ((uevent_sock = socket(PF_NETLINK, + SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { + LOGE("Unable to create uevent socket: %s", strerror(errno)); + exit(1); + } + + if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz, + sizeof(uevent_sz)) < 0) { + LOGE("Unable to set uevent socket options: %s", strerror(errno)); + exit(1); + } + + if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { + LOGE("Unable to bind uevent socket: %s", strerror(errno)); + exit(1); + } + + /* + * Bootstrap + */ + + // Volume Manager + volmgr_bootstrap(); + + // SD Card system + mmc_bootstrap(); + + // USB Mass Storage + ums_bootstrap(); + + // Switch + switch_bootstrap(); + + /* + * Main loop + */ + LOG_VOL("Bootstrapping complete"); + while(1) { + fd_set read_fds; + struct timeval to; + int max = 0; + int rc = 0; + + to.tv_sec = (60 * 60); + to.tv_usec = 0; + + FD_ZERO(&read_fds); + FD_SET(door_sock, &read_fds); + if (door_sock > max) + max = door_sock; + FD_SET(uevent_sock, &read_fds); + if (uevent_sock > max) + max = uevent_sock; + + if (fw_sock != -1) { + FD_SET(fw_sock, &read_fds); + if (fw_sock > max) + max = fw_sock; + } + + if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) { + LOGE("select() failed (%s)", strerror(errno)); + sleep(1); + continue; + } + + if (!rc) { + continue; + } + + if (FD_ISSET(door_sock, &read_fds)) { + struct sockaddr addr; + socklen_t alen; + + alen = sizeof(addr); + + if (fw_sock != -1) { + LOGE("Dropping duplicate framework connection"); + int tmp = accept(door_sock, &addr, &alen); + close(tmp); + continue; + } + + if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) { + LOGE("Unable to accept framework connection (%s)", + strerror(errno)); + } + LOG_VOL("Accepted connection from framework"); + if ((rc = volmgr_send_states()) < 0) { + LOGE("Unable to send volmgr status to framework (%d)", rc); + } + } + + if (FD_ISSET(fw_sock, &read_fds)) { + if ((rc = process_framework_command(fw_sock)) < 0) { + if (rc == -ECONNRESET) { + LOGE("Framework disconnected"); + close(fw_sock); + fw_sock = -1; + } else { + LOGE("Error processing framework command (%s)", + strerror(errno)); + } + } + } + + if (FD_ISSET(uevent_sock, &read_fds)) { + if ((rc = process_uevent_message(uevent_sock)) < 0) { + LOGE("Error processing uevent msg (%s)", strerror(errno)); + } + } + } // while + +} + +int send_msg(char* message) +{ + int result = -1; + + pthread_mutex_lock(&write_mutex); + + LOG_VOL("send_msg(%s):", message); + + if (fw_sock >= 0) + result = write(fw_sock, message, strlen(message) + 1); + + pthread_mutex_unlock(&write_mutex); + + return result; +} + +int send_msg_with_data(char *message, char *data) +{ + int result = -1; + + char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1); + if (!buffer) { + LOGE("alloca failed in send_msg_with_data"); + return -1; + } + + strcpy(buffer, message); + strcat(buffer, data); + return send_msg(buffer); +} diff --git a/vold/vold.h b/vold/vold.h new file mode 100644 index 0000000..0876bec --- /dev/null +++ b/vold/vold.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#ifndef VOLD_H__ +#define VOLD_H__ + +#define LOG_TAG "vold" +#include "cutils/log.h" + +typedef int boolean; +enum { + false = 0, + true = 1 +}; + +#define DEVPATH "/dev/block/" +#define DEVPATHLENGTH 11 + +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + +// Set this for logging error messages +#define ENABLE_LOG_ERROR + +// set this to log vold events +#define ENABLE_LOG_VOL + +#ifdef ENABLE_LOG_ERROR +#define LOG_ERROR(fmt, args...) \ + { LOGE(fmt , ## args); } +#else +#define LOG_ERROR(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ERROR */ + +#ifdef ENABLE_LOG_VOL +#define LOG_VOL(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_VOL(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_VOL */ + +#ifdef ENABLE_LOG_SERVER +#define LOG_SERVER(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_SERVER(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_SERVER */ + +#ifdef ENABLE_LOG_ASEC +#define LOG_ASEC(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_ASEC(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ASEC */ + +/* + * Prototypes + */ + +int process_framework_command(int socket); + +int process_inotify_event(int fd); +int inotify_bootstrap(void); + +int process_uevent_message(int socket); +int simulate_uevent(char *subsystem, char *path, char *action, char **params); + +int mmc_bootstrap(void); +int ums_bootstrap(void); + +int volmgr_bootstrap(void); + +int switch_bootstrap(void); + +void *read_file(char *filename, ssize_t *_size); +char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer); +char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var); + +void ums_hostconnected_set(boolean connected); +boolean ums_hostconnected_get(void); + +int send_msg(char *msg); +int send_msg_with_data(char *msg, char *data); +#endif diff --git a/vold/volmgr.c b/vold/volmgr.c new file mode 100644 index 0000000..c3ce8d1 --- /dev/null +++ b/vold/volmgr.c @@ -0,0 +1,1229 @@ + +/* + * 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 <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <sched.h> + +#include <sys/mount.h> + +#include <cutils/config_utils.h> +#include <cutils/properties.h> + +#include "vold.h" +#include "volmgr.h" +#include "blkdev.h" +#include "ums.h" +#include "format.h" +#include "devmapper.h" + +#include "volmgr_ext3.h" +#include "volmgr_vfat.h" + +#define DEBUG_VOLMGR 0 + +static volume_t *vol_root = NULL; +static boolean safe_mode = true; + +static struct volmgr_fstable_entry fs_table[] = { + { "ext3", ext_identify, ext_check, ext_mount , true }, + { "vfat", vfat_identify, vfat_check, vfat_mount , false }, + { NULL, NULL, NULL, NULL , false} +}; + +struct _volume_state_event_map { + volume_state_t state; + char *event; + char *property_val; +}; + +static struct _volume_state_event_map volume_state_strings[] = { + { volstate_unknown, "volstate_unknown:", "unknown" }, + { volstate_nomedia, VOLD_EVT_NOMEDIA, VOLD_ES_PVAL_NOMEDIA }, + { volstate_unmounted, VOLD_EVT_UNMOUNTED, VOLD_ES_PVAL_UNMOUNTED }, + { volstate_checking, VOLD_EVT_CHECKING, VOLD_ES_PVAL_CHECKING }, + { volstate_mounted, VOLD_EVT_MOUNTED, VOLD_ES_PVAL_MOUNTED }, + { volstate_mounted_ro, VOLD_EVT_MOUNTED_RO, VOLD_ES_PVAL_MOUNTED_RO }, + { volstate_badremoval, VOLD_EVT_BADREMOVAL, VOLD_ES_PVAL_BADREMOVAL }, + { volstate_damaged, VOLD_EVT_DAMAGED, VOLD_ES_PVAL_DAMAGED }, + { volstate_nofs, VOLD_EVT_NOFS, VOLD_ES_PVAL_NOFS }, + { volstate_ums, VOLD_EVT_UMS, VOLD_ES_PVAL_UMS }, + { 0, NULL, NULL } +}; + + +static int volmgr_readconfig(char *cfg_path); +static int volmgr_config_volume(cnode *node); +static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy); +static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev); +static int _volmgr_start(volume_t *vol, blkdev_t *dev); +static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev); +static void *volmgr_start_fs_thread(void *arg); +static void volmgr_start_fs_thread_sighandler(int signo); +static void volume_setstate(volume_t *vol, volume_state_t state); +static char *conv_volstate_to_eventstr(volume_state_t state); +static char *conv_volstate_to_propstr(volume_state_t state); +static int volume_send_state(volume_t *vol); +static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg); +static int _volmgr_enable_ums(volume_t *); +static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg), boolean emit_statechange); +static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange); +static void _cb_volume_stopped_for_eject(volume_t *v, void *arg); +static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg); +static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev); +static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg); +static void volmgr_reaper_thread_sighandler(int signo); +static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path); +static int volmgr_send_eject_request(volume_t *v); +static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked); + +static boolean _mountpoint_mounted(char *mp) +{ + char device[256]; + char mount_path[256]; + char rest[256]; + FILE *fp; + char line[1024]; + + if (!(fp = fopen("/proc/mounts", "r"))) { + LOGE("Error opening /proc/mounts (%s)", strerror(errno)); + return false; + } + + while(fgets(line, sizeof(line), fp)) { + line[strlen(line)-1] = '\0'; + sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); + if (!strcmp(mount_path, mp)) { + fclose(fp); + return true; + } + + } + + fclose(fp); + return false; +} + +/* + * Public functions + */ + +int volmgr_set_volume_key(char *mount_point, unsigned char *key) +{ + volume_t *v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + + if (!v) + return -ENOENT; + + if (v->media_type != media_devmapper) { + LOGE("Cannot set key on a non devmapper volume"); + pthread_mutex_unlock(&v->lock); + return -EINVAL; + } + + memcpy(v->dm->key, key, sizeof(v->dm->key)); + pthread_mutex_unlock(&v->lock); + return 0; +} + +int volmgr_format_volume(char *mount_point) +{ + int rc; + volume_t *v; + + LOG_VOL("volmgr_format_volume(%s):", mount_point); + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + + if (!v) + return -ENOENT; + + if (v->state == volstate_mounted || + v->state == volstate_mounted_ro || + v->state == volstate_ums || + v->state == volstate_checking) { + LOGE("Can't format '%s', currently in state %d", mount_point, v->state); + pthread_mutex_unlock(&v->lock); + return -EBUSY; + } else if (v->state == volstate_nomedia && + v->media_type != media_devmapper) { + LOGE("Can't format '%s', (no media)", mount_point); + pthread_mutex_unlock(&v->lock); + return -ENOMEDIUM; + } + + // XXX:Reject if the underlying source media is not present + + if (v->media_type == media_devmapper) { + if ((rc = devmapper_genesis(v->dm)) < 0) { + LOGE("devmapper genesis failed for %s (%d)", mount_point, rc); + pthread_mutex_unlock(&v->lock); + return rc; + } + } else { + if ((rc = initialize_mbr(v->dev->disk)) < 0) { + LOGE("MBR init failed for %s (%d)", mount_point, rc); + pthread_mutex_unlock(&v->lock); + return rc; + } + } + + volume_setstate(v, volstate_formatting); + pthread_mutex_unlock(&v->lock); + return rc; +} + +int volmgr_bootstrap(void) +{ + int rc; + + if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) { + LOGE("Unable to process config"); + return rc; + } + + /* + * Check to see if any of our volumes is mounted + */ + volume_t *v = vol_root; + while (v) { + if (_mountpoint_mounted(v->mount_point)) { + LOG_VOL("Volume '%s' already mounted at startup", v->mount_point); + v->state = volstate_mounted; + } + v = v->next; + } + + return 0; +} + +int volmgr_safe_mode(boolean enable) +{ + if (enable == safe_mode) + return 0; + + safe_mode = enable; + + volume_t *v = vol_root; + int rc; + + while (v) { + pthread_mutex_lock(&v->lock); + if (v->state == volstate_mounted && v->fs) { + rc = v->fs->mount_fn(v->dev, v, safe_mode); + if (!rc) { + LOG_VOL("Safe mode %s on %s", (enable ? "enabled" : "disabled"), v->mount_point); + } else { + LOGE("Failed to %s safe-mode on %s (%s)", + (enable ? "enable" : "disable" ), v->mount_point, strerror(-rc)); + } + } + + pthread_mutex_unlock(&v->lock); + v = v->next; + } + + return 0; +} + +int volmgr_send_states(void) +{ + volume_t *vol_scan = vol_root; + int rc; + + while (vol_scan) { + pthread_mutex_lock(&vol_scan->lock); + if ((rc = volume_send_state(vol_scan)) < 0) { + LOGE("Error sending state to framework (%d)", rc); + } + pthread_mutex_unlock(&vol_scan->lock); + vol_scan = vol_scan->next; + break; // XXX: + } + + return 0; +} + +/* + * Called when a block device is ready to be + * evaluated by the volume manager. + */ +int volmgr_consider_disk(blkdev_t *dev) +{ + volume_t *vol; + + if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true))) + return 0; + + pthread_mutex_lock(&vol->lock); + + if (vol->state == volstate_mounted) { + LOGE("Volume %s already mounted (did we just crash?)", vol->mount_point); + pthread_mutex_unlock(&vol->lock); + return 0; + } + + int rc = _volmgr_consider_disk_and_vol(vol, dev); + pthread_mutex_unlock(&vol->lock); + return rc; +} + +int volmgr_start_volume_by_mountpoint(char *mount_point) +{ + volume_t *v; + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + if (!v) + return -ENOENT; + + if (v->media_type == media_devmapper) { + if (devmapper_start(v->dm) < 0) { + LOGE("volmgr failed to start devmapper volume '%s'", + v->mount_point); + } + } else if (v->media_type == media_mmc) { + if (!v->dev) { + LOGE("Cannot start volume '%s' (volume is not bound)", mount_point); + pthread_mutex_unlock(&v->lock); + return -ENOENT; + } + + if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) { + LOGE("volmgr failed to start volume '%s'", v->mount_point); + } + } + + pthread_mutex_unlock(&v->lock); + return 0; +} + +static void _cb_volstopped_for_devmapper_teardown(volume_t *v, void *arg) +{ + devmapper_stop(v->dm); + volume_setstate(v, volstate_nomedia); + pthread_mutex_unlock(&v->lock); +} + +int volmgr_stop_volume_by_mountpoint(char *mount_point) +{ + int rc; + volume_t *v; + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + if (!v) + return -ENOENT; + + if (v->state == volstate_mounted) + volmgr_send_eject_request(v); + + if (v->media_type == media_devmapper) + rc = volmgr_shutdown_volume(v, _cb_volstopped_for_devmapper_teardown, false); + else + rc = volmgr_shutdown_volume(v, NULL, true); + + /* + * If shutdown returns -EINPROGRESS, + * do *not* release the lock as + * it is now owned by the reaper thread + */ + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + return 0; +} + +int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *)) +{ + LOG_VOL("Volmgr notified of %d:%d eject", dev->major, dev->minor); + + volume_t *v; + int rc; + + // XXX: Partitioning support is going to need us to stop *all* + // devices in this volume + if (!(v = volmgr_lookup_volume_by_dev(dev))) { + if (cb) + cb(dev); + return 0; + } + + pthread_mutex_lock(&v->lock); + + volume_state_t old_state = v->state; + + if (v->state == volstate_mounted || + v->state == volstate_ums || + v->state == volstate_checking) { + + volume_setstate(v, volstate_badremoval); + + /* + * Stop any devmapper volumes which + * are using us as a source + * XXX: We may need to enforce stricter + * order here + */ + volume_t *dmvol = vol_root; + while (dmvol) { + if ((dmvol->media_type == media_devmapper) && + (dmvol->dm->src_type == dmsrc_loopback) && + (!strncmp(dmvol->dm->type_data.loop.loop_src, + v->mount_point, strlen(v->mount_point)))) { + + pthread_mutex_lock(&dmvol->lock); + if (dmvol->state != volstate_nomedia) { + rc = volmgr_shutdown_volume(dmvol, _cb_volstopped_for_devmapper_teardown, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&dmvol->lock); + } + } else + pthread_mutex_unlock(&dmvol->lock); + } + dmvol = dmvol->next; + } + + } else if (v->state == volstate_formatting) { + /* + * The device is being ejected due to + * kernel disk revalidation. + */ + LOG_VOL("Volmgr ignoring eject of %d:%d (volume formatting)", + dev->major, dev->minor); + if (cb) + cb(dev); + pthread_mutex_unlock(&v->lock); + return 0; + } else + volume_setstate(v, volstate_nomedia); + + if (old_state == volstate_ums) { + ums_disable(v->ums_path); + pthread_mutex_unlock(&v->lock); + } else { + int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + } + return 0; +} + +static void _cb_volume_stopped_for_eject(volume_t *v, void *arg) +{ + void (* eject_cb) (blkdev_t *) = arg; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s has been stopped for eject", v->mount_point); +#endif + + if (eject_cb) + eject_cb(v->dev); + v->dev = NULL; // Clear dev because its being ejected +} + +/* + * Instructs the volume manager to enable or disable USB mass storage + * on any volumes configured to use it. + */ +int volmgr_enable_ums(boolean enable) +{ + volume_t *v = vol_root; + + while(v) { + if (v->ums_path) { + int rc; + + if (enable) { + pthread_mutex_lock(&v->lock); + if (v->state == volstate_mounted) + volmgr_send_eject_request(v); + else if (v->state == volstate_ums) { + pthread_mutex_unlock(&v->lock); + goto next_vol; + } + + // Stop the volume, and enable UMS in the callback + rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + } else { + // Disable UMS + pthread_mutex_lock(&v->lock); + if (v->state != volstate_ums) { + pthread_mutex_unlock(&v->lock); + goto next_vol; + } + + if ((rc = ums_disable(v->ums_path)) < 0) { + LOGE("unable to disable ums on '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + continue; + } + + LOG_VOL("Kick-starting volume %d:%d after UMS disable", + v->dev->disk->major, v->dev->disk->minor); + // Start volume + if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) { + LOGE("volmgr failed to consider disk %d:%d", + v->dev->disk->major, v->dev->disk->minor); + } + pthread_mutex_unlock(&v->lock); + } + } + next_vol: + v = v->next; + } + return 0; +} + +/* + * Static functions + */ + +static int volmgr_send_eject_request(volume_t *v) +{ + return send_msg_with_data(VOLD_EVT_EJECTING, v->mount_point); +} + +// vol->lock must be held! +static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev) +{ + int rc = 0; + +#if DEBUG_VOLMGR + LOG_VOL("volmgr_consider_disk_and_vol(%s, %d:%d):", vol->mount_point, + dev->major, dev->minor); +#endif + + if (vol->state == volstate_unknown || + vol->state == volstate_mounted || + vol->state == volstate_mounted_ro || + vol->state == volstate_damaged) { + LOGE("Cannot consider volume '%s' because it is in state '%d", + vol->mount_point, vol->state); + return -EADDRINUSE; + } + + if (vol->state == volstate_formatting) { + LOG_VOL("Evaluating dev '%s' for formattable filesystems for '%s'", + dev->devpath, vol->mount_point); + /* + * Since we only support creating 1 partition (right now), + * we can just lookup the target by devno + */ + blkdev_t *part = blkdev_lookup_by_devno(dev->major, 1); + if (!part) { + part = blkdev_lookup_by_devno(dev->major, 0); + if (!part) { + LOGE("Unable to find device to format"); + return -ENODEV; + } + } + + if ((rc = format_partition(part, + vol->media_type == media_devmapper ? + FORMAT_TYPE_EXT2 : FORMAT_TYPE_FAT32)) < 0) { + LOGE("format failed (%d)", rc); + return rc; + } + + } + + LOG_VOL("Evaluating dev '%s' for mountable filesystems for '%s'", + dev->devpath, vol->mount_point); + + if (dev->nr_parts == 0) { + rc = _volmgr_start(vol, dev); +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", vol->mount_point, + dev->major, dev->minor, rc); +#endif + } else { + /* + * Device has multiple partitions + * This is where interesting partition policies could be implemented. + * For now just try them in sequence until one succeeds + */ + + rc = -ENODEV; + int i; + for (i = 0; i < dev->nr_parts; i++) { + blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1)); + if (!part) { + LOGE("Error - unable to lookup partition for blkdev %d:%d", dev->major, (i+1)); + continue; + } + rc = _volmgr_start(vol, part); +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", + vol->mount_point, part->major, part->minor, rc); +#endif + if (!rc) + break; + } + + if (rc == -ENODEV) { + // Assert to make sure each partition had a backing blkdev + LOGE("Internal consistency error"); + return 0; + } + } + + if (rc == -ENODATA) { + LOGE("Device %d:%d contains no usable filesystems", + dev->major, dev->minor); + rc = 0; + } + + return rc; +} + +static void volmgr_reaper_thread_sighandler(int signo) +{ + LOGE("Volume reaper thread got signal %d", signo); +} + +static void __reaper_cleanup(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + if (vol->worker_args.reaper_args.cb) + vol->worker_args.reaper_args.cb(vol, vol->worker_args.reaper_args.cb_arg); + + vol->worker_running = false; + + // Wake up anyone that was waiting on this thread + pthread_mutex_unlock(&vol->worker_sem); + + // Unlock the volume + pthread_mutex_unlock(&vol->lock); +} + +static void *volmgr_reaper_thread(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + pthread_cleanup_push(__reaper_cleanup, arg); + + vol->worker_running = true; + vol->worker_pid = getpid(); + + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = volmgr_reaper_thread_sighandler; + sigaction(SIGUSR1, &actions, NULL); + + LOG_VOL("Reaper here - working on %s", vol->mount_point); + + boolean send_sig_kill = false; + int i, rc; + + for (i = 0; i < 10; i++) { + errno = 0; + rc = umount(vol->mount_point); + LOG_VOL("volmngr reaper umount(%s) attempt %d (%s)", + vol->mount_point, i + 1, strerror(errno)); + if (!rc) + break; + if (rc && (errno == EINVAL || errno == ENOENT)) { + rc = 0; + break; + } + sleep(1); + if (i >= 4) { + KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0); + if (!send_sig_kill) + send_sig_kill = true; + } + } + + if (!rc) { + LOG_VOL("Reaper sucessfully unmounted %s", vol->mount_point); + vol->fs = NULL; + volume_setstate(vol, volstate_unmounted); + } else { + LOGE("Unable to unmount!! (%d)", rc); + } + + out: + pthread_cleanup_pop(1); + pthread_exit(NULL); + return NULL; +} + +// vol->lock must be held! +static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg) +{ + + if (vol->worker_running) { + LOGE("Worker thread is currently running.. waiting.."); + pthread_mutex_lock(&vol->worker_sem); + LOG_VOL("Worker thread now available"); + } + + vol->worker_args.reaper_args.cb = cb; + vol->worker_args.reaper_args.cb_arg = arg; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol); +} + +static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange) +{ + int i, rc; + + if (v->state == volstate_mounted || v->state == volstate_badremoval) { + // Try to unmount right away (5 retries) + for (i = 0; i < 5; i++) { + rc = umount(v->mount_point); + if (!rc) + break; + + if (rc && (errno == EINVAL || errno == ENOENT)) { + rc = 0; + break; + } + + LOG_VOL("volmngr quick stop umount(%s) attempt %d (%s)", + v->mount_point, i + 1, strerror(errno)); + + if (i == 0) + usleep(1000 * 250); // First failure, sleep for 250 ms + else + sched_yield(); + } + + if (!rc) { + LOG_VOL("volmgr_stop_volume(%s): Volume unmounted sucessfully", + v->mount_point); + if (emit_statechange) + volume_setstate(v, volstate_unmounted); + v->fs = NULL; + goto out_cb_immed; + } + + /* + * Since the volume is still in use, dispatch the stopping to + * a thread + */ + LOG_VOL("Volume %s is busy (%d) - uncaging the reaper", v->mount_point, rc); + volmgr_uncage_reaper(v, cb, arg); + return -EINPROGRESS; + } else if (v->state == volstate_checking) { + volume_setstate(v, volstate_unmounted); + if (v->worker_running) { + LOG_VOL("Cancelling worker thread"); + pthread_kill(v->worker_thread, SIGUSR1); + } else + LOGE("Strange... we were in checking state but worker thread wasn't running.."); + goto out_cb_immed; + } + + out_cb_immed: + if (cb) + cb(v, arg); + return 0; +} + + +/* + * Gracefully stop a volume + * v->lock must be held! + * if we return -EINPROGRESS, do NOT release the lock as the reaper + * is using the volume + */ +static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *), boolean emit_statechange) +{ + return volmgr_stop_volume(v, cb, NULL, emit_statechange); +} + +static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg) +{ + void (* shutdown_cb) (volume_t *) = arg; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s has been stopped for shutdown", v->mount_point); +#endif + shutdown_cb(v); +} + + +/* + * Called when a volume is sucessfully unmounted for UMS enable + */ +static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg) +{ + int rc; + char *devdir_path; + +#if DEBUG_VOLMGR + LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point); +#endif + devdir_path = blkdev_get_devpath(v->dev->disk); + + if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) { + free(devdir_path); + LOGE("Error enabling ums (%d)", rc); + return; + } + free(devdir_path); + volume_setstate(v, volstate_ums); + pthread_mutex_unlock(&v->lock); +} + +static int volmgr_readconfig(char *cfg_path) +{ + cnode *root = config_node("", ""); + cnode *node; + + config_load_file(root, cfg_path); + node = root->first_child; + + while (node) { + if (!strncmp(node->name, "volume_", 7)) + volmgr_config_volume(node); + else + LOGE("Skipping unknown configuration node '%s'", node->name); + node = node->next; + } + return 0; +} + +static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path) +{ + int i; + +#if DEBUG_VOLMGR + LOG_VOL("volmgr_add_mediapath_to_volume(%p, %s):", v, media_path); +#endif + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (!v->media_paths[i]) { + v->media_paths[i] = strdup(media_path); + return; + } + } + LOGE("Unable to add media path '%s' to volume (out of media slots)", media_path); +} + +static int volmgr_config_volume(cnode *node) +{ + volume_t *new; + int rc = 0, i; + + char *dm_src, *dm_src_type, *dm_tgt, *dm_param, *dm_tgtfs; + uint32_t dm_size_mb = 0; + + dm_src = dm_src_type = dm_tgt = dm_param = dm_tgtfs = NULL; +#if DEBUG_VOLMGR + LOG_VOL("volmgr_configure_volume(%s):", node->name); +#endif + if (!(new = malloc(sizeof(volume_t)))) + return -ENOMEM; + memset(new, 0, sizeof(volume_t)); + + new->state = volstate_nomedia; + pthread_mutex_init(&new->lock, NULL); + pthread_mutex_init(&new->worker_sem, NULL); + + cnode *child = node->first_child; + + while (child) { + if (!strcmp(child->name, "media_path")) + volmgr_add_mediapath_to_volume(new, child->value); + else if (!strcmp(child->name, "emu_media_path")) + volmgr_add_mediapath_to_volume(new, child->value); + else if (!strcmp(child->name, "media_type")) { + if (!strcmp(child->value, "mmc")) + new->media_type = media_mmc; + else if (!strcmp(child->value, "devmapper")) + new->media_type = media_devmapper; + else { + LOGE("Invalid media type '%s'", child->value); + rc = -EINVAL; + goto out_free; + } + } else if (!strcmp(child->name, "mount_point")) + new->mount_point = strdup(child->value); + else if (!strcmp(child->name, "ums_path")) + new->ums_path = strdup(child->value); + else if (!strcmp(child->name, "dm_src")) + dm_src = strdup(child->value); + else if (!strcmp(child->name, "dm_src_type")) + dm_src_type = strdup(child->value); + else if (!strcmp(child->name, "dm_src_size_mb")) + dm_size_mb = atoi(child->value); + else if (!strcmp(child->name, "dm_target")) + dm_tgt = strdup(child->value); + else if (!strcmp(child->name, "dm_target_params")) + dm_param = strdup(child->value); + else if (!strcmp(child->name, "dm_target_fs")) + dm_tgtfs = strdup(child->value); + else + LOGE("Ignoring unknown config entry '%s'", child->name); + child = child->next; + } + + if (new->media_type == media_mmc) { + if (!new->media_paths[0] || !new->mount_point || new->media_type == media_unknown) { + LOGE("Required configuration parameter missing for mmc volume"); + rc = -EINVAL; + goto out_free; + } + } else if (new->media_type == media_devmapper) { + if (!dm_src || !dm_src_type || !dm_tgt || + !dm_param || !dm_tgtfs || !dm_size_mb) { + LOGE("Required configuration parameter missing for devmapper volume"); + rc = -EINVAL; + goto out_free; + } + + char dm_mediapath[255]; + if (!(new->dm = devmapper_init(dm_src, dm_src_type, dm_size_mb, + dm_tgt, dm_param, dm_tgtfs, dm_mediapath))) { + LOGE("Unable to initialize devmapping"); + goto out_free; + } + LOG_VOL("media path for devmapper volume = '%s'", dm_mediapath); + volmgr_add_mediapath_to_volume(new, dm_mediapath); + } + + if (!vol_root) + vol_root = new; + else { + volume_t *scan = vol_root; + while (scan->next) + scan = scan->next; + scan->next = new; + } + + if (dm_src) + free(dm_src); + if (dm_src_type) + free(dm_src_type); + if (dm_tgt) + free(dm_tgt); + if (dm_param) + free(dm_param); + if (dm_tgtfs) + free(dm_tgtfs); + + return rc; + + out_free: + + if (dm_src) + free(dm_src); + if (dm_src_type) + free(dm_src_type); + if (dm_tgt) + free(dm_tgt); + if (dm_param) + free(dm_param); + if (dm_tgtfs) + free(dm_tgtfs); + + + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (new->media_paths[i]) + free(new->media_paths[i]); + } + if (new->mount_point) + free(new->mount_point); + if (new->ums_path) + free(new->ums_path); + return rc; +} + +static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev) +{ + volume_t *scan = vol_root; + while(scan) { + if (scan->dev == dev) + return scan; + scan = scan->next; + } + return NULL; +} + +static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked) +{ + volume_t *v = vol_root; + + while(v) { + pthread_mutex_lock(&v->lock); + if (!strcmp(v->mount_point, mount_point)) { + if (!leave_locked) + pthread_mutex_unlock(&v->lock); + return v; + } + pthread_mutex_unlock(&v->lock); + v = v->next; + } + return NULL; +} + +static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy) +{ + volume_t *scan = vol_root; + int i; + + while (scan) { + + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (!scan->media_paths[i]) + continue; + + if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i]))) + return scan; + else if (!fuzzy && !strcmp(media_path, scan->media_paths[i])) + return scan; + } + + scan = scan->next; + } + return NULL; +} + +/* + * Attempt to bring a volume online + * Returns: 0 on success, errno on failure, with the following exceptions: + * - ENODATA - Unsupported filesystem type / blank + * vol->lock MUST be held! + */ +static int _volmgr_start(volume_t *vol, blkdev_t *dev) +{ + struct volmgr_fstable_entry *fs; + int rc = ENODATA; + +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d):", vol->mount_point, + dev->major, dev->minor); +#endif + + if (vol->state == volstate_mounted) { + LOGE("Unable to start volume '%s' (already mounted)", vol->mount_point); + return -EBUSY; + } + + for (fs = fs_table; fs->name; fs++) { + if (!fs->identify_fn(dev)) + break; + } + + if (!fs) { + LOGE("No supported filesystems on %d:%d", dev->major, dev->minor); + volume_setstate(vol, volstate_nofs); + return -ENODATA; + } + + return volmgr_start_fs(fs, vol, dev); +} + +// vol->lock MUST be held! +static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev) +{ + /* + * Spawn a thread to do the actual checking / mounting in + */ + + if (vol->worker_running) { + LOGE("Worker thread is currently running.. waiting.."); + pthread_mutex_lock(&vol->worker_sem); + LOG_VOL("Worker thread now available"); + } + + vol->dev = dev; + + vol->worker_args.start_args.fs = fs; + vol->worker_args.start_args.dev = dev; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol); + + return 0; +} + +static void __start_fs_thread_lock_cleanup(void *arg) +{ + volume_t *vol = (volume_t *) arg; + +#if DEBUG_VOLMGR + LOG_VOL("__start_fs_thread_lock_cleanup(%s):", vol->mount_point); +#endif + + vol->worker_running = false; + + // Wake up anyone that was waiting on this thread + pthread_mutex_unlock(&vol->worker_sem); + + // Unlock the volume + pthread_mutex_unlock(&vol->lock); +} + +static void *volmgr_start_fs_thread(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg); + pthread_mutex_lock(&vol->lock); + + vol->worker_running = true; + vol->worker_pid = getpid(); + + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = volmgr_start_fs_thread_sighandler; + sigaction(SIGUSR1, &actions, NULL); + + struct volmgr_fstable_entry *fs = vol->worker_args.start_args.fs; + blkdev_t *dev = vol->worker_args.start_args.dev; + int rc; + +#if DEBUG_VOLMGR + LOG_VOL("Worker thread pid %d starting %s fs %d:%d on %s", getpid(), + fs->name, dev->major, dev->minor, vol->mount_point); +#endif + + if (fs->check_fn) { +#if DEBUG_VOLMGR + LOG_VOL("Starting %s filesystem check on %d:%d", fs->name, + dev->major, dev->minor); +#endif + volume_setstate(vol, volstate_checking); + pthread_mutex_unlock(&vol->lock); + rc = fs->check_fn(dev); + pthread_mutex_lock(&vol->lock); + if (vol->state != volstate_checking) { + LOG_VOL("filesystem check aborted"); + goto out; + } + + if (rc < 0) { + LOGE("%s filesystem check failed on %d:%d (%s)", fs->name, + dev->major, dev->minor, strerror(-rc)); + if (rc == -ENODATA) { + volume_setstate(vol, volstate_nofs); + goto out; + } + goto out_unmountable; + } +#if DEBUG_VOLMGR + LOG_VOL("%s filesystem check of %d:%d OK", fs->name, + dev->major, dev->minor); +#endif + } + + rc = fs->mount_fn(dev, vol, safe_mode); + if (!rc) { + LOG_VOL("Sucessfully mounted %s filesystem %d:%d on %s (safe-mode %s)", + fs->name, dev->major, dev->minor, vol->mount_point, + (safe_mode ? "on" : "off")); + vol->fs = fs; + volume_setstate(vol, volstate_mounted); + goto out; + } + + LOGE("%s filesystem mount of %d:%d failed (%d)", fs->name, dev->major, + dev->minor, rc); + + out_unmountable: + volume_setstate(vol, volstate_damaged); + out: + pthread_cleanup_pop(1); + pthread_exit(NULL); + return NULL; +} + +static void volmgr_start_fs_thread_sighandler(int signo) +{ + LOGE("Volume startup thread got signal %d", signo); +} + +static void volume_setstate(volume_t *vol, volume_state_t state) +{ + if (state == vol->state) + return; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s state change from %d -> %d", vol->mount_point, vol->state, state); +#endif + + vol->state = state; + + char *prop_val = conv_volstate_to_propstr(vol->state); + + if (prop_val) { + property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val); + volume_send_state(vol); + } +} + +static int volume_send_state(volume_t *vol) +{ + char *event = conv_volstate_to_eventstr(vol->state); + + return send_msg_with_data(event, vol->mount_point); +} + +static char *conv_volstate_to_eventstr(volume_state_t state) +{ + int i; + + for (i = 0; volume_state_strings[i].event != NULL; i++) { + if (volume_state_strings[i].state == state) + break; + } + + return volume_state_strings[i].event; +} + +static char *conv_volstate_to_propstr(volume_state_t state) +{ + int i; + + for (i = 0; volume_state_strings[i].event != NULL; i++) { + if (volume_state_strings[i].state == state) + break; + } + + return volume_state_strings[i].property_val; +} + diff --git a/vold/volmgr.h b/vold/volmgr.h new file mode 100644 index 0000000..2c7ec50 --- /dev/null +++ b/vold/volmgr.h @@ -0,0 +1,135 @@ + +/* + * 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. + */ + +#ifndef _VOLMGR_H +#define _VOLMGR_H + +#include <pthread.h> + +#include "vold.h" +#include "blkdev.h" +#include "media.h" +#include "devmapper.h" + +#define PROP_EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE" + +// these must match the corresponding states in the MediaState enum. +// A path to the volume mount point follows the colon +typedef enum volume_state { + volstate_unknown, + + volstate_nomedia, +#define VOLD_EVT_NOMEDIA "volume_nomedia:" +#define VOLD_ES_PVAL_NOMEDIA "removed" + + volstate_unmounted, +#define VOLD_EVT_UNMOUNTED "volume_unmounted:" +#define VOLD_ES_PVAL_UNMOUNTED "unmounted" + + volstate_checking, +#define VOLD_EVT_CHECKING "volume_checking:" +#define VOLD_ES_PVAL_CHECKING "checking" + + volstate_mounted, +#define VOLD_EVT_MOUNTED "volume_mounted:" +#define VOLD_ES_PVAL_MOUNTED "mounted" + + volstate_mounted_ro, +#define VOLD_EVT_MOUNTED_RO "volume_mounted_ro:" +#define VOLD_ES_PVAL_MOUNTED_RO "mounted_ro" + + volstate_badremoval, +#define VOLD_EVT_BADREMOVAL "volume_badremoval:" +#define VOLD_ES_PVAL_BADREMOVAL "bad_removal" + + volstate_damaged, +#define VOLD_EVT_DAMAGED "volume_damaged:" +#define VOLD_ES_PVAL_DAMAGED "unmountable" + + volstate_nofs, +#define VOLD_EVT_NOFS "volume_nofs:" +#define VOLD_ES_PVAL_NOFS "nofs" + + volstate_ums, +#define VOLD_EVT_UMS "volume_ums:" +#define VOLD_ES_PVAL_UMS "shared" + + volstate_ejecting, +#define VOLD_EVT_EJECTING "volume_ejecting:" +#define VOLD_ES_PVAL_EJECTING "ejecting" + + volstate_formatting, +} volume_state_t; + +struct volume; + +struct volmgr_fstable_entry { + char *name; + int (*identify_fn) (blkdev_t *dev); + int (*check_fn) (blkdev_t *dev); + int (*mount_fn) (blkdev_t *dev, struct volume *vol, boolean safe_mode); + boolean case_sensitive_paths; +}; + +struct volmgr_start_args { + struct volmgr_fstable_entry *fs; + blkdev_t *dev; +}; + +struct volmgr_reaper_args { + void (*cb) (struct volume *, void *); + void *cb_arg; +}; + +#define VOLMGR_MAX_MEDIAPATHS_PER_VOLUME 8 + +typedef struct volume { + char *media_paths[VOLMGR_MAX_MEDIAPATHS_PER_VOLUME]; + + media_type_t media_type; + char *mount_point; + char *ums_path; + struct devmapping *dm; + + pthread_mutex_t lock; + volume_state_t state; + blkdev_t *dev; + pid_t worker_pid; + pthread_t worker_thread; + union { + struct volmgr_start_args start_args; + struct volmgr_reaper_args reaper_args; + } worker_args; + boolean worker_running; + pthread_mutex_t worker_sem; + + struct volmgr_fstable_entry *fs; + + struct volume *next; +} volume_t; + +int volmgr_consider_disk(blkdev_t *dev); +int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *)); +int volmgr_send_states(void); +int volmgr_enable_ums(boolean enable); +int volmgr_stop_volume_by_mountpoint(char *mount_point); +int volmgr_start_volume_by_mountpoint(char *mount_point); +int volmgr_safe_mode(boolean enable); +int volmgr_format_volume(char *mount_point); +int volmgr_set_volume_key(char *mount_point, unsigned char *key); +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded); +#endif diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c new file mode 100644 index 0000000..680be21 --- /dev/null +++ b/vold/volmgr_ext3.c @@ -0,0 +1,184 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mount.h> + +#include <linux/ext2_fs.h> +#include <linux/ext3_fs.h> + +#include "vold.h" +#include "volmgr.h" +#include "volmgr_ext3.h" +#include "logwrapper.h" + + +#define EXT_DEBUG 0 + +static char E2FSCK_PATH[] = "/system/bin/e2fsck"; + +int ext_identify(blkdev_t *dev) +{ + int rc = -1; + int fd; + struct ext3_super_block sb; + char *devpath; + +#if EXT_DEBUG + LOG_VOL("ext_identify(%d:%d):", dev-major, dev->minor); +#endif + + devpath = blkdev_get_devpath(dev); + + if ((fd = open(devpath, O_RDWR)) < 0) { + LOGE("Unable to open device '%s' (%s)", devpath, + strerror(errno)); + free(devpath); + return -errno; + } + + if (lseek(fd, 1024, SEEK_SET) < 0) { + LOGE("Unable to lseek to get superblock (%s)", strerror(errno)); + rc = -errno; + goto out; + } + + if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { + LOGE("Unable to read superblock (%s)", strerror(errno)); + rc = -errno; + goto out; + } + + if (sb.s_magic == EXT2_SUPER_MAGIC || + sb.s_magic == EXT3_SUPER_MAGIC) + rc = 0; + else + rc = -ENODATA; + + out: +#if EXT_DEBUG + LOG_VOL("ext_identify(%s): rc = %d", devpath, rc); +#endif + free(devpath); + close(fd); + return rc; +} + +int ext_check(blkdev_t *dev) +{ + char *devpath; + +#if EXT_DEBUG + LOG_VOL("ext_check(%s):", dev->dev_fspath); +#endif + + devpath = blkdev_get_devpath(dev); + + if (access(E2FSCK_PATH, X_OK)) { + LOGE("ext_check(%s): %s not found (skipping checks)", + devpath, E2FSCK_PATH); + free(devpath); + return 0; + } + + char *args[5]; + + args[0] = E2FSCK_PATH; + args[1] = "-v"; + args[2] = "-p"; + args[3] = devpath; + args[4] = NULL; + + int rc = logwrap(4, args); + + if (rc == 0) { + LOG_VOL("filesystem '%s' had no errors", devpath); + } else if (rc == 1) { + LOG_VOL("filesystem '%s' had corrected errors", devpath); + rc = 0; + } else if (rc == 2) { + LOGE("VOL volume '%s' had corrected errors (system should be rebooted)", devpath); + rc = -EIO; + } else if (rc == 4) { + LOGE("VOL volume '%s' had uncorrectable errors", devpath); + rc = -EIO; + } else if (rc == 8) { + LOGE("Operational error while checking volume '%s'", devpath); + rc = -EIO; + } else { + LOGE("Unknown e2fsck exit code (%d)", rc); + rc = -EIO; + } + free(devpath); + return rc; +} + +int ext_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode) +{ +#if EXT_DEBUG + LOG_VOL("ext_mount(%s, %s, %d):", dev->dev_fspath, vol->mount_point, safe_mode); +#endif + + char *fs[] = { "ext3", "ext2", NULL }; + char *devpath; + + devpath = blkdev_get_devpath(dev); + + int flags, rc = 0; + + flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME; + + if (safe_mode) + flags |= MS_SYNCHRONOUS; + + if (vol->state == volstate_mounted) { + LOG_VOL("Remounting %s on %s, safe mode %d", devpath, + vol->mount_point, safe_mode); + flags |= MS_REMOUNT; + } + + char **f; + for (f = fs; *f != NULL; f++) { + rc = mount(devpath, vol->mount_point, *f, flags, NULL); + if (rc && errno == EROFS) { + LOGE("ext_mount(%s, %s): Read only filesystem - retrying mount RO", + devpath, vol->mount_point); + flags |= MS_RDONLY; + rc = mount(devpath, vol->mount_point, *f, flags, NULL); + } +#if EXT_DEBUG + LOG_VOL("ext_mount(%s, %s): %s mount rc = %d", devpath, *f, + vol->mount_point, rc); +#endif + if (!rc) + break; + } + free(devpath); + + // Chmod the mount point so that its a free-for-all. + // (required for consistency with VFAT.. sigh) + if (chmod(vol->mount_point, 0777) < 0) { + LOGE("Failed to chmod %s (%s)", vol->mount_point, strerror(errno)); + return -errno; + } + + return rc; +} diff --git a/vold/volmgr_ext3.h b/vold/volmgr_ext3.h new file mode 100644 index 0000000..bfe882a --- /dev/null +++ b/vold/volmgr_ext3.h @@ -0,0 +1,27 @@ + +/* + * 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. + */ + +#ifndef _VOLMGR_EXT3_H +#define _VOLMGR_EXT3_H + +#include "volmgr.h" +#include "blkdev.h" + +int ext_identify(blkdev_t *blkdev); +int ext_check(blkdev_t *blkdev); +int ext_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); +#endif diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c new file mode 100644 index 0000000..1dc4c8d --- /dev/null +++ b/vold/volmgr_vfat.c @@ -0,0 +1,135 @@ + +/* + * 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 <errno.h> + +#include <sys/mount.h> + +#include "vold.h" +#include "volmgr.h" +#include "volmgr_vfat.h" +#include "logwrapper.h" + +#define VFAT_DEBUG 0 + +static char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck"; + +int vfat_identify(blkdev_t *dev) +{ +#if VFAT_DEBUG + LOG_VOL("vfat_identify(%d:%d):", dev->major, dev->minor); +#endif + return 0; // XXX: Implement +} + +int vfat_check(blkdev_t *dev) +{ + int rc; + +#if VFAT_DEBUG + LOG_VOL("vfat_check(%d:%d):", dev->major, dev->minor); +#endif + + if (access(FSCK_MSDOS_PATH, X_OK)) { + LOGE("vfat_check(%d:%d): %s not found (skipping checks)", + dev->major, dev->minor, FSCK_MSDOS_PATH); + return 0; + } + +#ifdef VERIFY_PASS + char *args[7]; + args[0] = FSCK_MSDOS_PATH; + args[1] = "-v"; + args[2] = "-V"; + args[3] = "-w"; + args[4] = "-p"; + args[5] = blkdev_get_devpath(dev); + args[6] = NULL; + rc = logwrap(6, args); + free(args[5]); +#else + char *args[6]; + args[0] = FSCK_MSDOS_PATH; + args[1] = "-v"; + args[2] = "-w"; + args[3] = "-p"; + args[4] = blkdev_get_devpath(dev); + args[5] = NULL; + rc = logwrap(5, args); + free(args[4]); +#endif + + if (rc == 0) { + LOG_VOL("Filesystem check completed OK"); + return 0; + } else if (rc == 1) { + LOG_VOL("Filesystem check failed (general failure)"); + return -EINVAL; + } else if (rc == 2) { + LOG_VOL("Filesystem check failed (invalid usage)"); + return -EIO; + } else if (rc == 4) { + LOG_VOL("Filesystem check completed (errors fixed)"); + } else if (rc == 8) { + LOG_VOL("Filesystem check failed (not a FAT filesystem)"); + return -ENODATA; + } else { + LOG_VOL("Filesystem check failed (unknown exit code %d)", rc); + return -EIO; + } + return 0; +} + +int vfat_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode) +{ + int flags, rc; + char *devpath; + + devpath = blkdev_get_devpath(dev); + +#if VFAT_DEBUG + LOG_VOL("vfat_mount(%d:%d, %s, %d):", dev->major, dev->minor, vol->mount_point, safe_mode); +#endif + + flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; + + if (safe_mode) + flags |= MS_SYNCHRONOUS; + if (vol->state == volstate_mounted) { + LOG_VOL("Remounting %d:%d on %s, safe mode %d", dev->major, + dev->minor, vol->mount_point, safe_mode); + flags |= MS_REMOUNT; + } + + rc = mount(devpath, vol->mount_point, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + + if (rc && errno == EROFS) { + LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO", + dev->major, dev->minor, vol->mount_point); + flags |= MS_RDONLY; + rc = mount(devpath, vol->mount_point, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + } + +#if VFAT_DEBUG + LOG_VOL("vfat_mount(%s, %d:%d): mount rc = %d", dev->major,k dev->minor, + vol->mount_point, rc); +#endif + free (devpath); + return rc; +} diff --git a/vold/volmgr_vfat.h b/vold/volmgr_vfat.h new file mode 100644 index 0000000..d9cf04d --- /dev/null +++ b/vold/volmgr_vfat.h @@ -0,0 +1,29 @@ + +/* + * 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. + */ + +#ifndef _VOLMGR_VFAT_H +#define _VOLMGR_VFAT_H + +#include "volmgr.h" +#include "blkdev.h" + + + +int vfat_identify(blkdev_t *blkdev); +int vfat_check(blkdev_t *blkdev); +int vfat_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); +#endif |