// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
// the retouched entries back to their original value (such that SHA-1 checks
// don't fail due to randomization); store the file contents and associated
// metadata in *file.
//
// Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file,
                     int retouch_flag) {
    file->data = NULL;

    // A special 'filename' beginning with "MTD:" or "EMMC:" means to
    // load the contents of a partition.
    if (strncmp(filename, "MTD:", 4) == 0 ||
        strncmp(filename, "EMMC:", 5) == 0) {
        return LoadPartitionContents(filename, file);
    }

    if (stat(filename, &file->st) != 0) {
        printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
        return -1;
    }

    file->size = file->st.st_size;
    file->data = malloc(file->size);

    FILE* f = fopen(filename, "rb");
    if (f == NULL) {
        printf("failed to open \"%s\": %s\n", filename, strerror(errno));
        free(file->data);
        file->data = NULL;
        return -1;
    }

    ssize_t bytes_read = fread(file->data, 1, file->size, f);
    if (bytes_read != file->size) {
        printf("short read of \"%s\" (%ld bytes of %ld)\n",
               filename, (long)bytes_read, (long)file->size);
        free(file->data);
        file->data = NULL;
        return -1;
    }
    fclose(f);

    // apply_patch[_check] functions are blind to randomization. Randomization
    // is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
    // within a file, this means the file is assumed "corrupt" for simplicity.
    if (retouch_flag) {
        int32_t desired_offset = 0;
        if (retouch_mask_data(file->data, file->size,
                              &desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
            printf("error trying to mask retouch entries\n");
            free(file->data);
            file->data = NULL;
            return -1;
        }
    }

    SHA_hash(file->data, file->size, file->sha1);
    return 0;
}
/*
 * This function flashes a given image to the target partition. It verifies
 * the target cheksum first, and will return if target has the desired hash.
 * It checks the checksum of the given source image before flashing, and
 * verifies the target partition afterwards. The function is idempotent.
 * Returns zero on success.
 */
int applypatch_flash(const char* source_filename, const char* target_filename,
                     const char* target_sha1_str, size_t target_size) {
    printf("flash %s: ", target_filename);

    uint8_t target_sha1[SHA_DIGEST_SIZE];
    if (ParseSha1(target_sha1_str, target_sha1) != 0) {
        printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
        return 1;
    }

    FileContents source_file;
    source_file.data = NULL;
    std::string target_str(target_filename);

    std::vector<std::string> pieces = android::base::Split(target_str, ":");
    if (pieces.size() != 2 || (pieces[0] != "MTD" && pieces[0] != "EMMC")) {
        printf("invalid target name \"%s\"", target_filename);
        return 1;
    }

    // Load the target into the source_file object to see if already applied.
    pieces.push_back(std::to_string(target_size));
    pieces.push_back(target_sha1_str);
    std::string fullname = android::base::Join(pieces, ':');
    if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 &&
        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
        // The early-exit case: the image was already applied, this partition
        // has the desired hash, nothing for us to do.
        printf("already %s\n", short_sha1(target_sha1).c_str());
        free(source_file.data);
        return 0;
    }

    if (LoadFileContents(source_filename, &source_file) == 0) {
        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
            // The source doesn't have desired checksum.
            printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
            printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
                    short_sha1(source_file.sha1).c_str());
            free(source_file.data);
            return 1;
        }
    }

    if (WriteToPartition(source_file.data, target_size, target_filename) != 0) {
        printf("write of copied data to %s failed\n", target_filename);
        free(source_file.data);
        return 1;
    }

    free(source_file.data);
    return 0;
}
// Read a file into memory; store the file contents and associated
// metadata in *file.
//
// Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file) {
    file->data = NULL;

    // A special 'filename' beginning with "MTD:" or "EMMC:" means to
    // load the contents of a partition.
    if (strncmp(filename, "MTD:", 4) == 0 ||
        strncmp(filename, "EMMC:", 5) == 0) {
        return LoadPartitionContents(filename, file);
    }

    if (stat(filename, &file->st) != 0) {
        printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
        return -1;
    }

    file->size = file->st.st_size;
    file->data = malloc(file->size);

    FILE* f = fopen(filename, "rb");
    if (f == NULL) {
        printf("failed to open \"%s\": %s\n", filename, strerror(errno));
        free(file->data);
        file->data = NULL;
        return -1;
    }

    ssize_t bytes_read = fread(file->data, 1, file->size, f);
    if (bytes_read != file->size) {
        printf("short read of \"%s\" (%ld bytes of %ld)\n",
               filename, (long)bytes_read, (long)file->size);
        free(file->data);
        file->data = NULL;
        return -1;
    }
    fclose(f);

    SHA_hash(file->data, file->size, file->sha1);
    return 0;
}