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); }
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); }
// 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); }
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; }
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; }
// 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 }
/* 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; }