int format_device(const char *device, const char *path, const char *fs_type) {
    Volume* v = volume_for_path(path);
    if (v == NULL) {
        // silent failure for sd-ext
        if (strcmp(path, "/sd-ext") == 0)
            return -1;
        LOGE("未知卷 \"%s\"\n", path);
        return -1;
    }
    if (is_data_media_volume_path(path)) {
        return format_unknown_device(NULL, path, NULL);
    }
    if (strstr(path, "/data") == path && is_data_media()) {
        return format_unknown_device(NULL, path, NULL);
    }
    if (strcmp(fs_type, "ramdisk") == 0) {
        // you can't format the ramdisk.
        LOGE("无法格式化卷 \"%s\"", path);
        return -1;
    }

    if (strcmp(fs_type, "rfs") == 0) {
        if (ensure_path_unmounted(path) != 0) {
            LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
            return -1;
        }
        if (0 != format_rfs_device(device, path)) {
            LOGE("format_volume: format_rfs_device failed on %s\n", device);
            return -1;
        }
        return 0;
    }
 
    if (strcmp(v->mount_point, path) != 0) {
        return format_unknown_device(v->device, path, NULL);
    }

    if (ensure_path_unmounted(path) != 0) {
        LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
        return -1;
    }

    if (strcmp(fs_type, "yaffs2") == 0 || strcmp(fs_type, "mtd") == 0) {
        mtd_scan_partitions();
        const MtdPartition* partition = mtd_find_partition_by_name(device);
        if (partition == NULL) {
            LOGE("format_volume: no MTD partition \"%s\"\n", device);
            return -1;
        }

        MtdWriteContext *write = mtd_write_partition(partition);
        if (write == NULL) {
            LOGW("format_volume: can't open MTD \"%s\"\n", device);
            return -1;
        } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
            LOGW("format_volume: can't erase MTD \"%s\"\n", device);
            mtd_write_close(write);
            return -1;
        } else if (mtd_write_close(write)) {
            LOGW("format_volume: can't close MTD \"%s\"\n",device);
            return -1;
        }
        return 0;
    }

    if (strcmp(fs_type, "ext4") == 0) {
        int length = 0;
        if (strcmp(v->fs_type, "ext4") == 0) {
            // Our desired filesystem matches the one in fstab, respect v->length
            length = v->length;
        }
        reset_ext4fs_info();
        int result = make_ext4fs(device, length, v->mount_point, sehandle);
        if (result != 0) {
            LOGE("format_volume: make_extf4fs failed on %s\n", device);
            return -1;
        }
        return 0;
    }

    return format_unknown_device(device, path, fs_type);
}
int format_device(const char *device, const char *path, const char *fs_type) {
    Volume* v = volume_for_path(path);
    if (v == NULL) {
        // no /sdcard? let's assume /data/media
        if (strstr(path, "/sdcard") == path && is_data_media()) {
            return format_unknown_device(NULL, path, NULL);
        }
        // silent failure for sd-ext
        if (strcmp(path, "/sd-ext") == 0)
            return -1;
        LOGE("unknown volume \"%s\"\n", path);
        return -1;
    }
    if (strcmp(fs_type, "ramdisk") == 0) {
        // you can't format the ramdisk.
        LOGE("can't format_volume \"%s\"", path);
        return -1;
    }

    if (strcmp(fs_type, "rfs") == 0) {
        if (ensure_path_unmounted(path) != 0) {
            LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
            return -1;
        }
        if (0 != format_rfs_device(device, path)) {
            LOGE("format_volume: format_rfs_device failed on %s\n", device);
            return -1;
        }
        return 0;
    }
 
    if (strcmp(v->mount_point, path) != 0) {
        return format_unknown_device(v->device, path, NULL);
    }

    if (ensure_path_unmounted(path) != 0) {
        LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
        return -1;
    }

    if (strcmp(fs_type, "yaffs2") == 0 || strcmp(fs_type, "mtd") == 0) {
        mtd_scan_partitions();
        const MtdPartition* partition = mtd_find_partition_by_name(device);
        if (partition == NULL) {
            LOGE("format_volume: no MTD partition \"%s\"\n", device);
            return -1;
        }

        MtdWriteContext *write = mtd_write_partition(partition);
        if (write == NULL) {
            LOGW("format_volume: can't open MTD \"%s\"\n", device);
            return -1;
        } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
            LOGW("format_volume: can't erase MTD \"%s\"\n", device);
            mtd_write_close(write);
            return -1;
        } else if (mtd_write_close(write)) {
            LOGW("format_volume: can't close MTD \"%s\"\n",device);
            return -1;
        }
        return 0;
    }

    if (strcmp(fs_type, "ext4") == 0) {
        reset_ext4fs_info();
        int result = make_ext4fs(device, NULL, NULL, 0, 0, 0);
        if (result != 0) {
            LOGE("format_volume: make_extf4fs failed on %s\n", device);
            return -1;
        }
        return 0;
    }

    return format_unknown_device(device, path, fs_type);
}
Exemplo n.º 3
0
int format_volume(const char* volume) {
    Volume* v = volume_for_path(volume);
    if (v == NULL) {
        // silent failure for sd-ext
        if (strcmp(volume, "/sd-ext") == 0)
            return -1;
        LOGE("unknown volume \"%s\"\n", volume);
        return -1;
    }
    if (is_data_media_volume_path(volume)) {
        return format_unknown_device(NULL, volume, NULL);
    }
    // check to see if /data is being formatted, and if it is /data/media
    // Note: the /external_sd check is redundant probably, just being safe.
    if (strstr(volume, "/data") == volume && is_data_media() && !handle_data_media) {
        return format_unknown_device(NULL, volume, NULL);
    }
    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) {
#if 0
        LOGE("can't give path \"%s\" to format_volume\n", volume);
        return -1;
#endif
        return format_unknown_device(v->device, volume, NULL);
    }

    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, volume, sehandle);
        if (result != 0) {
            LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
            return -1;
        }
        return 0;
    }

#if 0
    LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
    return -1;
#endif
    return format_unknown_device(v->device, volume, v->fs_type);
}
Exemplo n.º 4
0
// Write a memory buffer to 'target' partition, a string of the form
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:".  Return 0 on
// success.
int WriteToPartition(unsigned char* data, size_t len,
                        const char* target) {
    char* copy = strdup(target);
    const char* magic = strtok(copy, ":");

    enum PartitionType type;
    if (strcmp(magic, "MTD") == 0) {
        type = MTD;
    } else if (strcmp(magic, "EMMC") == 0) {
        type = EMMC;
    } else {
        printf("WriteToPartition called with bad target (%s)\n", target);
        return -1;
    }

    const char* partition = strtok(NULL, ":");

    if (partition == NULL) {
        printf("bad partition target name \"%s\"\n", target);
#if 1
        free(copy);
#endif
        return -1;

    }

    char *dev_path = get_partition_path(partition);
    partition = dev_path;

    switch (type) {
        case MTD:
            if (!mtd_partitions_scanned) {
                mtd_scan_partitions();
                mtd_partitions_scanned = 1;
            }

            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
            if (mtd == NULL) {
                printf("mtd partition \"%s\" not found for writing\n",
                       partition);
#if 1
                free(copy);
                free(partition);
#endif
                return -1;
            }

            MtdWriteContext* ctx = mtd_write_partition(mtd);
            if (ctx == NULL) {
                printf("failed to init mtd partition \"%s\" for writing\n",
                       partition);
#if 1
                free(copy);
                free(partition);
#endif
                return -1;
            }

            size_t written = mtd_write_data(ctx, (char*)data, len);
            if (written != len) {
                printf("only wrote %zu of %zu bytes to MTD %s\n",
                       written, len, partition);
                mtd_write_close(ctx);
#if 1
                free(copy);
                free(partition);
#endif
                return -1;
            }

            if (mtd_erase_blocks(ctx, -1) < 0) {
                printf("error finishing mtd write of %s\n", partition);
                mtd_write_close(ctx);
#if 1
                free(copy);
                free(partition);
#endif
                return -1;
            }

            if (mtd_write_close(ctx)) {
                printf("error closing mtd write of %s\n", partition);
#if 1
                free(copy);
                free(partition);
#endif
                return -1;
            }
            break;

        case EMMC:
        {
            size_t start = 0;
            int success = 0;
            int fd = open(partition, O_RDWR | O_SYNC);
            if (fd < 0) {
                printf("failed to open %s: %s\n", partition, strerror(errno));
                return -1;
            }
            int attempt;

            for (attempt = 0; attempt < 2; ++attempt) {
                if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
                    printf("failed seek on %s: %s\n",
                           partition, strerror(errno));
                    close(fd);
                    return -1;
                }
                while (start < len) {
                    size_t to_write = len - start;
                    if (to_write > 1<<20) to_write = 1<<20;

                    ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write));
                    if (written == -1) {
                        printf("failed write writing to %s: %s\n", partition, strerror(errno));
                        close(fd);
                        return -1;
                    }
                    start += written;
                }
                if (fsync(fd) != 0) {
                   printf("failed to sync to %s (%s)\n",
                          partition, strerror(errno));
                    close(fd);
                   return -1;
                }
                if (close(fd) != 0) {
                   printf("failed to close %s (%s)\n",
                          partition, strerror(errno));
                   return -1;
                }
                fd = open(partition, O_RDONLY);
                if (fd < 0) {
                   printf("failed to reopen %s for verify (%s)\n",
                          partition, strerror(errno));
                   return -1;
                }

                // drop caches so our subsequent verification read
                // won't just be reading the cache.
                sync();
                int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
                if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) {
                    printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
                } else {
                    printf("  caches dropped\n");
                }
                close(dc);
                sleep(1);

                // verify
                if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
                    printf("failed to seek back to beginning of %s: %s\n",
                           partition, strerror(errno));
                    close(fd);
                    return -1;
                }
                unsigned char buffer[4096];
                start = len;
                size_t p;
                for (p = 0; p < len; p += sizeof(buffer)) {
                    size_t to_read = len - p;
                    if (to_read > sizeof(buffer)) to_read = sizeof(buffer);

                    size_t so_far = 0;
                    while (so_far < to_read) {
                        ssize_t read_count =
                                TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far));
                        if (read_count == -1) {
                            printf("verify read error %s at %zu: %s\n",
                                   partition, p, strerror(errno));
                            close(fd);
                            return -1;
                        }
                        if ((size_t)read_count < to_read) {
                            printf("short verify read %s at %zu: %zd %zu %s\n",
                                   partition, p, read_count, to_read, strerror(errno));
                        }
                        so_far += read_count;
                    }

                    if (memcmp(buffer, data+p, to_read)) {
                        printf("verification failed starting at %zu\n", p);
                        start = p;
                        break;
                    }
                }

                if (start == len) {
                    printf("verification read succeeded (attempt %d)\n", attempt+1);
                    success = true;
                    break;
                }
            }

            if (!success) {
                printf("failed to verify after all attempts\n");
                close(fd);
                return -1;
            }

            if (close(fd) != 0) {
                printf("error closing %s (%s)\n", partition, strerror(errno));
                return -1;
            }
            sync();
            break;
        }
    }

    free(copy);
    return 0;
}
int
format_root_device(const char *root)
{
    /* Be a little safer here; require that "root" is just
     * a device with no relative path after it.
     */
    const char *c = root;
    while (*c != '\0' && *c != ':') {
        c++;
    }
    /*
    if (c[0] != ':' || c[1] != '\0') {
        LOGW("format_root_device: bad root name \"%s\"\n", root);
        return -1;
    }
    */

    const RootInfo *info = get_root_info_for_path(root);
    if (info == NULL || info->device == NULL) {
        LOGW("format_root_device: can't resolve \"%s\"\n", root);
        return -1;
    }
    if (info->mount_point != NULL && *info->device == '@') {
        /* Don't try to format a mounted device.
         */
        int ret = ensure_root_path_unmounted(root);
        if (ret < 0) {
            LOGW("format_root_device: can't unmount \"%s\"\n", root);
            return ret;
        }
    }

    /* Format the device.
     */
    if (*info->device == '@') {
        mtd_scan_partitions();
        const MtdPartition *partition;
        partition = mtd_find_partition_by_name(info->partition_name);
        if (partition == NULL) {
            LOGW("format_root_device: can't find mtd partition \"%s\"\n",
                    info->partition_name);
            return -1;
        }
        if (info->filesystem == g_raw || !strcmp(info->filesystem, "yaffs2")) {
            MtdWriteContext *write = mtd_write_partition(partition);
            if (write == NULL) {
                LOGW("format_root_device: can't open \"%s\"\n", root);
                return -1;
            } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
                LOGW("format_root_device: can't erase \"%s\"\n", root);
                mtd_write_close(write);
                return -1;
            } else if (mtd_write_close(write)) {
                LOGW("format_root_device: can't close \"%s\"\n", root);
                return -1;
            } else {
                return 0;
            }
        }
    }
    
    return format_non_mtd_device(root);
}
Exemplo n.º 6
0
int write_update_for_bootloader(
    const char *update, int update_length,
    int bitmap_width, int bitmap_height, int bitmap_bpp,
    const char *busy_bitmap, const char *fail_bitmap) {
    if (ensure_root_path_unmounted(CACHE_NAME)) {
        LOGE("Can't unmount %s\n", CACHE_NAME);
        return -1;
    }

    const MtdPartition *part = get_root_mtd_partition(CACHE_NAME);
    if (part == NULL) {
        LOGE("Can't find %s\n", CACHE_NAME);
        return -1;
    }

    MtdWriteContext *write = mtd_write_partition(part);
    if (write == NULL) {
        LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno));
        return -1;
    }

    /* Write an invalid (zero) header first, to disable any previous
     * update and any other structured contents (like a filesystem),
     * and as a placeholder for the amount of space required.
     */

    struct update_header header;
    memset(&header, 0, sizeof(header));
    const ssize_t header_size = sizeof(header);
    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
        LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    /* Write each section individually block-aligned, so we can write
     * each block independently without complicated buffering.
     */

    memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
    header.version = UPDATE_VERSION;
    header.size = header_size;

    header.image_offset = mtd_erase_blocks(write, 0);
    header.image_length = update_length;
    if ((int) header.image_offset == -1 ||
        mtd_write_data(write, update, update_length) != update_length) {
        LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    header.bitmap_width = bitmap_width;
    header.bitmap_height = bitmap_height;
    header.bitmap_bpp = bitmap_bpp;

    int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;

    header.busy_bitmap_offset = mtd_erase_blocks(write, 0);
    header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
    if ((int) header.busy_bitmap_offset == -1 ||
        mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
        LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    header.fail_bitmap_offset = mtd_erase_blocks(write, 0);
    header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
    if ((int) header.fail_bitmap_offset == -1 ||
        mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
        LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    /* Write the header last, after all the blocks it refers to, so that
     * when the magic number is installed everything is valid.
     */

    if (mtd_write_close(write)) {
        LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno));
        return -1;
    }

    write = mtd_write_partition(part);
    if (write == NULL) {
        LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno));
        return -1;
    }

    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
        LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) {
        LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
        mtd_write_close(write);
        return -1;
    }

    if (mtd_write_close(write)) {
        LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
        return -1;
    }

    return 0;
}
Exemplo n.º 7
0
int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
{
    const MtdPartition *ptn;
    MtdWriteContext *write;
    void *data;
    unsigned sz;

    if (mtd_scan_partitions() <= 0)
    {
        printf("error scanning partitions");
        return -1;
    }
    const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
    if (partition == NULL)
    {
        printf("can't find %s partition", partition_name);
        return -1;
    }

    int fd = open(filename, O_RDONLY);
    if (fd < 0)
    {
        printf("error opening %s", filename);
        return -1;
    }

    char header[HEADER_SIZE];
    int headerlen = read(fd, header, sizeof(header));
    if (headerlen <= 0)
    {
        printf("error reading %s header", filename);
        return -1;
    }

    // Skip the header (we'll come back to it), write everything else
    printf("flashing %s from %s\n", partition_name, filename);

    MtdWriteContext *out = mtd_write_partition(partition);
    if (out == NULL)
    {
       printf("error writing %s", partition_name);
       return -1;
    }

    char buf[HEADER_SIZE];
    memset(buf, 0, headerlen);
    int wrote = mtd_write_data(out, buf, headerlen);
    if (wrote != headerlen)
    {
        printf("error writing %s", partition_name);
        return -1;
    }

    int len;
    while ((len = read(fd, buf, sizeof(buf))) > 0) {
        wrote = mtd_write_data(out, buf, len);
        if (wrote != len)
        {
            printf("error writing %s", partition_name);
            return -1;
        }
    }
    if (len < 0)
    {
       printf("error reading %s", filename);
       return -1;
    }

    if (mtd_write_close(out))
    {
        printf("error closing %s", partition_name);
        return -1;
    }

    // Now come back and write the header last

    out = mtd_write_partition(partition);
    if (out == NULL)
    {
        printf("error re-opening %s", partition_name);
        return -1;
    }

    wrote = mtd_write_data(out, header, headerlen);
    if (wrote != headerlen)
    {
        printf("error re-writing %s", partition_name);
        return -1;
    }

    // Need to write a complete block, so write the rest of the first block
    size_t block_size;
    if (mtd_partition_info(partition, NULL, &block_size, NULL))
    {
        printf("error getting %s block size", partition_name);
        return -1;
    }

    if (lseek(fd, headerlen, SEEK_SET) != headerlen)
    {
        printf("error rewinding %s", filename);
        return -1;
    }

    int left = block_size - headerlen;
    while (left < 0) left += block_size;
    while (left > 0) {
        len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
        if (len == 0)
	    break;
	if (len < 0) {
            printf("error reading %s", filename);
            return -1;
        }
        if (mtd_write_data(out, buf, len) != len)
        {
            printf("error writing %s", partition_name);
            return -1;
        }

        left -= len;
    }

    if (mtd_write_close(out))
    {
        printf("error closing %s", partition_name);
        return -1;
    }
    return 0;
}
Exemplo n.º 8
0
// Write a memory buffer to target_mtd partition, a string of the form
// "MTD:<partition>[:...]".  Return 0 on success.
int WriteToMTDPartition(unsigned char* data, size_t len,
                        const char* target_mtd) {
#ifdef BOARD_USES_MTDUTILS
    char* partition = strchr(target_mtd, ':');
    if (partition == NULL) {
        printf("bad MTD target name \"%s\"\n", target_mtd);
        return -1;
    }
    ++partition;
    // Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
    // We want just the partition name "boot".
    partition = strdup(partition);
    char* end = strchr(partition, ':');
    if (end != NULL)
        *end = '\0';

    if (!mtd_partitions_scanned) {
        mtd_scan_partitions();
        mtd_partitions_scanned = 1;
    }

    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
    if (mtd == NULL) {
        printf("mtd partition \"%s\" not found for writing\n", partition);
        return -1;
    }

    MtdWriteContext* ctx = mtd_write_partition(mtd);
    if (ctx == NULL) {
        printf("failed to init mtd partition \"%s\" for writing\n",
               partition);
        return -1;
    }

    size_t written = mtd_write_data(ctx, (char*)data, len);
    if (written != len) {
        printf("only wrote %d of %d bytes to MTD %s\n",
               written, len, partition);
        mtd_write_close(ctx);
        return -1;
    }

    if (mtd_erase_blocks(ctx, -1) < 0) {
        printf("error finishing mtd write of %s\n", partition);
        mtd_write_close(ctx);
        return -1;
    }

    if (mtd_write_close(ctx)) {
        printf("error closing mtd write of %s\n", partition);
        return -1;
    }

    free(partition);
    return 0;
#else
    printf("mtd utils not supported.\n");
    return -1;
#endif

}
Exemplo n.º 9
0
/* Read an image file and write it to a flash partition. */
int main(int argc, char **argv) {
    const MtdPartition *ptn;
    MtdWriteContext *write;
    void *data;
    unsigned sz;
    int i;
    char *partitionName = NULL, *imageFile = NULL;
    int deleteImage = 0;

    if (argc < 3 || argc > 4) {
		printUsage(argv[0]);
        return 2;
    }

	for (i=1; i<argc; i++) {
		if (!strcmp(argv[i], "-d"))
			deleteImage = 1;
		else if (partitionName == NULL)
			partitionName = argv[i];
		else
			imageFile = argv[i];
	}

	if (partitionName == NULL || imageFile == NULL) {
		printUsage(argv[0]);
		return 2;
	}



    if (mtd_scan_partitions() <= 0) die("error scanning partitions");
    const MtdPartition *partition = mtd_find_partition_by_name(partitionName);
    if (partition == NULL) die("can't find %s partition", partitionName);

    // If the first part of the file matches the partition, skip writing

    int fd = open(imageFile, O_RDONLY);
    if (fd < 0) die("error opening %s", imageFile);

    char header[HEADER_SIZE];
    int headerlen = read(fd, header, sizeof(header));
    if (headerlen <= 0) die("error reading %s header", imageFile);

    MtdReadContext *in = mtd_read_partition(partition);
    if (in == NULL) {
        LOGW("error opening %s: %s\n", partitionName, strerror(errno));
        // just assume it needs re-writing
    } else {
        char check[HEADER_SIZE];
        int checklen = mtd_read_data(in, check, sizeof(check));
        if (checklen <= 0) {
            LOGW("error reading %s: %s\n", partitionName, strerror(errno));
            // just assume it needs re-writing
        } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
            LOGI("header is the same, not flashing %s\n", argv[1]);
            if (deleteImage)
				unlink(imageFile);
            return 0;
        }
        mtd_read_close(in);
    }

    // Skip the header (we'll come back to it), write everything else
    LOGI("flashing %s from %s\n", partitionName, imageFile);

    MtdWriteContext *out = mtd_write_partition(partition);
    if (out == NULL) die("error writing %s", partitionName);

    char buf[HEADER_SIZE];
    memset(buf, 0, headerlen);
    int wrote = mtd_write_data(out, buf, headerlen);
    if (wrote != headerlen) die("error writing %s", partitionName);

    int len;
    while ((len = read(fd, buf, sizeof(buf))) > 0) {
        wrote = mtd_write_data(out, buf, len);
        if (wrote != len) die("error writing %s", partitionName);
    }
    if (len < 0) die("error reading %s", imageFile);

    if (mtd_write_close(out)) die("error closing %s", partitionName);

    // Now come back and write the header last

    out = mtd_write_partition(partition);
    if (out == NULL) die("error re-opening %s", partitionName);

    wrote = mtd_write_data(out, header, headerlen);
    if (wrote != headerlen) die("error re-writing %s", partitionName);

    // Need to write a complete block, so write the rest of the first block
    size_t block_size;
    if (mtd_partition_info(partition, NULL, &block_size, NULL))
        die("error getting %s block size", partitionName);

    if (lseek(fd, headerlen, SEEK_SET) != headerlen)
        die("error rewinding %s", imageFile);

    int left = block_size - headerlen;
    while (left < 0) left += block_size;
    while (left > 0) {
        len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
        if (len <= 0) die("error reading %s", imageFile);
        if (mtd_write_data(out, buf, len) != len)
            die("error writing %s", partitionName);
        left -= len;
    }

    if (mtd_write_close(out)) die("error closing %s", partitionName);

	if (deleteImage)
		unlink(imageFile);

    return 0;
}
// Write a memory buffer to 'target' partition, a string of the form
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:".  Return 0 on
// success.
int WriteToPartition(unsigned char* data, size_t len,
                        const char* target) {
    char* copy = strdup(target);
    const char* magic = strtok(copy, ":");

    enum PartitionType type;
    if (strcmp(magic, "MTD") == 0) {
        type = MTD;
    } else if (strcmp(magic, "EMMC") == 0) {
        type = EMMC;
    } else {
        printf("WriteToPartition called with bad target (%s)\n", target);
        return -1;
    }
    const char* partition = strtok(NULL, ":");

    if (partition == NULL) {
        printf("bad partition target name \"%s\"\n", target);
        return -1;
    }

    switch (type) {
        case MTD:
            if (!mtd_partitions_scanned) {
                mtd_scan_partitions();
                mtd_partitions_scanned = 1;
            }

            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
            if (mtd == NULL) {
                printf("mtd partition \"%s\" not found for writing\n",
                       partition);
                return -1;
            }

            MtdWriteContext* ctx = mtd_write_partition(mtd);
            if (ctx == NULL) {
                printf("failed to init mtd partition \"%s\" for writing\n",
                       partition);
                return -1;
            }

            size_t written = mtd_write_data(ctx, (char*)data, len);
            if (written != len) {
                printf("only wrote %zu of %zu bytes to MTD %s\n",
                       written, len, partition);
                mtd_write_close(ctx);
                return -1;
            }

            if (mtd_erase_blocks(ctx, -1) < 0) {
                printf("error finishing mtd write of %s\n", partition);
                mtd_write_close(ctx);
                return -1;
            }

            if (mtd_write_close(ctx)) {
                printf("error closing mtd write of %s\n", partition);
                return -1;
            }
            break;

        case EMMC:
        {
            size_t start = 0;
            int success = 0;
            int fd = open(partition, O_RDWR | O_SYNC);
            if (fd < 0) {
                printf("failed to open %s: %s\n", partition, strerror(errno));
                return -1;
            }
            int attempt;

            for (attempt = 0; attempt < 10; ++attempt) {
                size_t next_sync = start + (1<<20);
                printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start);
                lseek(fd, start, SEEK_SET);
                while (start < len) {
                    size_t to_write = len - start;
                    if (to_write > 4096) to_write = 4096;

                    ssize_t written = write(fd, data+start, to_write);
                    if (written < 0) {
                        if (errno == EINTR) {
                            written = 0;
                        } else {
                            printf("failed write writing to %s (%s)\n",
                                   partition, strerror(errno));
                            return -1;
                        }
                    }
                    start += written;
                    if (start >= next_sync) {
                        fsync(fd);
                        next_sync = start + (1<<20);
                    }
                }
                fsync(fd);

                // drop caches so our subsequent verification read
                // won't just be reading the cache.
                sync();
                int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
                write(dc, "3\n", 2);
                close(dc);
                sleep(1);
                printf("  caches dropped\n");

                // verify
                lseek(fd, 0, SEEK_SET);
                unsigned char buffer[4096];
                start = len;
                size_t p;
                for (p = 0; p < len; p += sizeof(buffer)) {
                    size_t to_read = len - p;
                    if (to_read > sizeof(buffer)) to_read = sizeof(buffer);

                    size_t so_far = 0;
                    while (so_far < to_read) {
                        ssize_t read_count = read(fd, buffer+so_far, to_read-so_far);
                        if (read_count < 0) {
                            if (errno == EINTR) {
                                read_count = 0;
                            } else {
                                printf("verify read error %s at %zu: %s\n",
                                       partition, p, strerror(errno));
                                return -1;
                            }
                        }
                        if ((size_t)read_count < to_read) {
                            printf("short verify read %s at %zu: %zd %zu %s\n",
                                   partition, p, read_count, to_read, strerror(errno));
                        }
                        so_far += read_count;
                    }

                    if (memcmp(buffer, data+p, to_read)) {
                        printf("verification failed starting at %zu\n", p);
                        start = p;
                        break;
                    }
                }

                if (start == len) {
                    printf("verification read succeeded (attempt %d)\n", attempt+1);
                    success = true;
                    break;
                }

                sleep(2);
            }

            if (!success) {
                printf("failed to verify after all attempts\n");
                return -1;
            }

            if (close(fd) != 0) {
                printf("error closing %s (%s)\n", partition, strerror(errno));
                return -1;
            }
            // hack: sync and sleep after closing in hopes of getting
            // the data actually onto flash.
            printf("sleeping after close\n");
            sync();
            sleep(5);
            break;
        }
    }

    free(copy);
    return 0;
}
int main(int argc, char **argv) {
	const MtdPartition *ptn;
	MtdWriteContext *write;
	void *data;
	unsigned sz;
	int rc = 0;

	if (argc != 3) {
		fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
		return -EINVAL;
	}

	rc = mtd_scan_partitions();
	if (rc < 0) {
		fprintf(stderr, "error scanning partitions\n");
		return rc;
	} else if (rc == 0) {
		fprintf(stderr, "no partitions found\n");
		return -ENODEV;
	}

	const MtdPartition *partition = mtd_find_partition_by_name(argv[1]);
	if (partition == NULL) {
		fprintf(stderr, "can't find %s partition\n", argv[1]);
		return -ENODEV;
	}

	// If the first part of the file matches the partition, skip writing

	int fd = open(argv[2], O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "error opening %s\n", argv[2]);
		return fd;
	}

	char header[HEADER_SIZE];
	int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
	if (headerlen <= 0) {
		fprintf(stderr, "error reading %s header\n", argv[2]);
		rc = -EIO;
		goto exit;
	}

	MtdReadContext *in = mtd_read_partition(partition);
	if (in == NULL) {
		fprintf(stderr, "error opening %s: %s\n", argv[1], strerror(errno));
		rc = -ENXIO;
		goto exit;
		// just assume it needs re-writing
	} else {
		char check[HEADER_SIZE];
		int checklen = mtd_read_data(in, check, sizeof(check));
		if (checklen <= 0) {
			fprintf(stderr, "error reading %s: %s\n", argv[1], strerror(errno));
			rc = -EIO;
			goto exit;
			// just assume it needs re-writing
		} else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
			fprintf(stderr, "header is the same, not flashing %s\n", argv[1]);
			rc = -EINVAL;
			goto exit;
		}
		mtd_read_close(in);
	}

	// Skip the header (we'll come back to it), write everything else
	printf("flashing %s from %s\n", argv[1], argv[2]);

	MtdWriteContext *out = mtd_write_partition(partition);
	if (out == NULL) {
		fprintf(stderr, "error writing %s\n", argv[1]);
		rc = -EIO;
		goto exit;
	}

	char buf[HEADER_SIZE];
	memset(buf, 0, headerlen);
	int wrote = mtd_write_data(out, buf, headerlen);
	if (wrote != headerlen) {
		fprintf(stderr, "error writing %s\n", argv[1]);
		rc = -EIO;
		goto exit;
	}

	int len;
	while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
		wrote = mtd_write_data(out, buf, len);
		if (wrote != len) {
			fprintf(stderr, "error writing %s\n", argv[1]);
			rc = -EIO;
			goto exit;
		}
	}
	if (len < 0) {
		fprintf(stderr, "error reading %s\n", argv[2]);
		rc = -EIO;
		goto exit;
	}

	rc = mtd_write_close(out);
	if (rc < 0) {
		fprintf(stderr, "error closing %s\n", argv[1]);
		goto exit;
	}

	// Now come back and write the header last

	out = mtd_write_partition(partition);
	if (out == NULL) {
		fprintf(stderr, "error re-opening %s\n", argv[1]);
		rc = -EIO;
		goto exit;
	}

	wrote = mtd_write_data(out, header, headerlen);
	if (wrote != headerlen) {
		fprintf(stderr, "error re-writing %s\n", argv[1]);
		rc = -EIO;
		goto exit;
	}

	// Need to write a complete block, so write the rest of the first block
	size_t block_size;
	rc = mtd_partition_info(partition, NULL, &block_size, NULL);
	if (rc < 0) {
		fprintf(stderr, "error getting %s block size\n", argv[1]);
		goto exit;
	}

	if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen) {
		fprintf(stderr, "error rewinding %s\n", argv[2]);
		rc = -ESPIPE;
		goto exit;
	}

	int left = block_size - headerlen;
	while (left < 0) left += block_size;
	while (left > 0) {
		len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
		if (len <= 0) {
			fprintf(stderr, "error reading %s\n", argv[2]);
			rc = -EIO;
			goto exit;
		}
		if (mtd_write_data(out, buf, len) != len) {
			fprintf(stderr, "error writing %s\n", argv[1]);
			rc = -EIO;
			goto exit;
		}
		left -= len;
	}

	rc = mtd_write_close(out);
	if (rc < 0) {
		fprintf(stderr, "error closing %s\n", argv[1]);
		goto exit;
	}

	rc = 0;

exit:
	close(fd);
	return rc;
}
// Write a memory buffer to 'target' partition, a string of the form
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:".  Return 0 on
// success.
int WriteToPartition(unsigned char* data, size_t len,
                        const char* target) {
    char* copy = strdup(target);
    const char* magic = strtok(copy, ":");

    enum PartitionType type;
    if (strcmp(magic, "MTD") == 0) {
        type = MTD;
    } else if (strcmp(magic, "EMMC") == 0) {
        type = EMMC;
    } else {
        printf("WriteToPartition called with bad target (%s)\n", target);
        return -1;
    }
    const char* partition = strtok(NULL, ":");

    if (partition == NULL) {
        printf("bad partition target name \"%s\"\n", target);
        return -1;
    }

    switch (type) {
        case MTD:
            if (!mtd_partitions_scanned) {
                mtd_scan_partitions();
                mtd_partitions_scanned = 1;
            }

            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
            if (mtd == NULL) {
                printf("mtd partition \"%s\" not found for writing\n",
                       partition);
                return -1;
            }

            MtdWriteContext* ctx = mtd_write_partition(mtd);
            if (ctx == NULL) {
                printf("failed to init mtd partition \"%s\" for writing\n",
                       partition);
                return -1;
            }

            size_t written = mtd_write_data(ctx, (char*)data, len);
            if (written != len) {
                printf("only wrote %d of %d bytes to MTD %s\n",
                       written, len, partition);
                mtd_write_close(ctx);
                return -1;
            }

            if (mtd_erase_blocks(ctx, -1) < 0) {
                printf("error finishing mtd write of %s\n", partition);
                mtd_write_close(ctx);
                return -1;
            }

            if (mtd_write_close(ctx)) {
                printf("error closing mtd write of %s\n", partition);
                return -1;
            }
            break;

        case EMMC:
            ;
            FILE* f = fopen(partition, "wb");
            if (fwrite(data, 1, len, f) != len) {
                printf("short write writing to %s (%s)\n",
                       partition, strerror(errno));
                return -1;
            }
            if (fclose(f) != 0) {
                printf("error closing %s (%s)\n", partition, strerror(errno));
                return -1;
            }
            break;
    }

    free(copy);
    return 0;
}