diff options
Diffstat (limited to 'roots.cpp')
-rw-r--r-- | roots.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/roots.cpp b/roots.cpp new file mode 100644 index 000000000..9345cb0a2 --- /dev/null +++ b/roots.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2007 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 <stdlib.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <ctype.h> + +#include "mtdutils/mtdutils.h" +#include "mtdutils/mounts.h" +#include "roots.h" +#include "common.h" +#include "make_ext4fs.h" + +static int num_volumes = 0; +static Volume* device_volumes = NULL; + +static int parse_options(char* options, Volume* volume) { + char* option; + while ((option = strtok(options, ","))) { + options = NULL; + + if (strncmp(option, "length=", 7) == 0) { + volume->length = strtoll(option+7, NULL, 10); + } else { + LOGE("bad option \"%s\"\n", option); + return -1; + } + } + return 0; +} + +void load_volume_table() { + int alloc = 2; + device_volumes = (Volume*)malloc(alloc * sizeof(Volume)); + + // Insert an entry for /tmp, which is the ramdisk and is always mounted. + device_volumes[0].mount_point = "/tmp"; + device_volumes[0].fs_type = "ramdisk"; + device_volumes[0].device = NULL; + device_volumes[0].device2 = NULL; + device_volumes[0].length = 0; + num_volumes = 1; + + FILE* fstab = fopen("/etc/recovery.fstab", "r"); + if (fstab == NULL) { + LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno)); + return; + } + + char buffer[1024]; + int i; + while (fgets(buffer, sizeof(buffer)-1, fstab)) { + for (i = 0; buffer[i] && isspace(buffer[i]); ++i); + if (buffer[i] == '\0' || buffer[i] == '#') continue; + + char* original = strdup(buffer); + + char* mount_point = strtok(buffer+i, " \t\n"); + char* fs_type = strtok(NULL, " \t\n"); + char* device = strtok(NULL, " \t\n"); + // lines may optionally have a second device, to use if + // mounting the first one fails. + char* options = NULL; + char* device2 = strtok(NULL, " \t\n"); + if (device2) { + if (device2[0] == '/') { + options = strtok(NULL, " \t\n"); + } else { + options = device2; + device2 = NULL; + } + } + + if (mount_point && fs_type && device) { + while (num_volumes >= alloc) { + alloc *= 2; + device_volumes = (Volume*)realloc(device_volumes, alloc*sizeof(Volume)); + } + device_volumes[num_volumes].mount_point = strdup(mount_point); + device_volumes[num_volumes].fs_type = strdup(fs_type); + device_volumes[num_volumes].device = strdup(device); + device_volumes[num_volumes].device2 = + device2 ? strdup(device2) : NULL; + + device_volumes[num_volumes].length = 0; + if (parse_options(options, device_volumes + num_volumes) != 0) { + LOGE("skipping malformed recovery.fstab line: %s\n", original); + } else { + ++num_volumes; + } + } else { + LOGE("skipping malformed recovery.fstab line: %s\n", original); + } + free(original); + } + + fclose(fstab); + + printf("recovery filesystem table\n"); + printf("=========================\n"); + for (i = 0; i < num_volumes; ++i) { + Volume* v = &device_volumes[i]; + printf(" %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type, + v->device, v->device2, v->length); + } + printf("\n"); +} + +Volume* volume_for_path(const char* path) { + int i; + for (i = 0; i < num_volumes; ++i) { + Volume* v = device_volumes+i; + int len = strlen(v->mount_point); + if (strncmp(path, v->mount_point, len) == 0 && + (path[len] == '\0' || path[len] == '/')) { + return v; + } + } + return NULL; +} + +int ensure_path_mounted(const char* path) { + Volume* v = volume_for_path(path); + if (v == NULL) { + LOGE("unknown volume for path [%s]\n", path); + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // the ramdisk is always mounted. + return 0; + } + + int result; + result = scan_mounted_volumes(); + if (result < 0) { + LOGE("failed to scan mounted volumes\n"); + return -1; + } + + const MountedVolume* mv = + find_mounted_volume_by_mount_point(v->mount_point); + if (mv) { + // volume is already mounted + return 0; + } + + mkdir(v->mount_point, 0755); // in case it doesn't already exist + + if (strcmp(v->fs_type, "yaffs2") == 0) { + // mount an MTD partition as a YAFFS2 filesystem. + mtd_scan_partitions(); + const MtdPartition* partition; + partition = mtd_find_partition_by_name(v->device); + if (partition == NULL) { + LOGE("failed to find \"%s\" partition to mount at \"%s\"\n", + v->device, v->mount_point); + return -1; + } + return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0); + } else if (strcmp(v->fs_type, "ext4") == 0 || + strcmp(v->fs_type, "vfat") == 0) { + result = mount(v->device, v->mount_point, v->fs_type, + MS_NOATIME | MS_NODEV | MS_NODIRATIME, ""); + if (result == 0) return 0; + + if (v->device2) { + LOGW("failed to mount %s (%s); trying %s\n", + v->device, strerror(errno), v->device2); + result = mount(v->device2, v->mount_point, v->fs_type, + MS_NOATIME | MS_NODEV | MS_NODIRATIME, ""); + if (result == 0) return 0; + } + + LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno)); + return -1; + } + + LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point); + return -1; +} + +int ensure_path_unmounted(const char* path) { + Volume* v = volume_for_path(path); + if (v == NULL) { + LOGE("unknown volume for path [%s]\n", path); + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // the ramdisk is always mounted; you can't unmount it. + return -1; + } + + int result; + result = scan_mounted_volumes(); + if (result < 0) { + LOGE("failed to scan mounted volumes\n"); + return -1; + } + + const MountedVolume* mv = + find_mounted_volume_by_mount_point(v->mount_point); + if (mv == NULL) { + // volume is already unmounted + return 0; + } + + return unmount_mounted_volume(mv); +} + +int format_volume(const char* volume) { + Volume* v = volume_for_path(volume); + if (v == NULL) { + LOGE("unknown volume \"%s\"\n", volume); + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // you can't format the ramdisk. + LOGE("can't format_volume \"%s\"", volume); + return -1; + } + if (strcmp(v->mount_point, volume) != 0) { + LOGE("can't give path \"%s\" to format_volume\n", volume); + return -1; + } + + if (ensure_path_unmounted(volume) != 0) { + LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point); + return -1; + } + + if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) { + mtd_scan_partitions(); + const MtdPartition* partition = mtd_find_partition_by_name(v->device); + if (partition == NULL) { + LOGE("format_volume: no MTD partition \"%s\"\n", v->device); + return -1; + } + + MtdWriteContext *write = mtd_write_partition(partition); + if (write == NULL) { + LOGW("format_volume: can't open MTD \"%s\"\n", v->device); + return -1; + } else if (mtd_erase_blocks(write, -1) == (off_t) -1) { + LOGW("format_volume: can't erase MTD \"%s\"\n", v->device); + mtd_write_close(write); + return -1; + } else if (mtd_write_close(write)) { + LOGW("format_volume: can't close MTD \"%s\"\n", v->device); + return -1; + } + return 0; + } + + if (strcmp(v->fs_type, "ext4") == 0) { + int result = make_ext4fs(v->device, v->length); + if (result != 0) { + LOGE("format_volume: make_extf4fs failed on %s\n", v->device); + return -1; + } + return 0; + } + + LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type); + return -1; +} |