lr_yum_repo_free(LrYumRepo *repo)
{
    if (!repo)
        return;

    for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
        LrYumRepoPath *yumrepopath = elem->data;
        assert(yumrepopath);
        lr_free(yumrepopath->type);
        lr_free(yumrepopath->path);
        lr_free(yumrepopath);
    }

    g_slist_free(repo->paths);
    lr_free(repo->repomd);
    lr_free(repo->url);
    lr_free(repo->destdir);
    lr_free(repo->signature);
    lr_free(repo->mirrorlist);
    lr_free(repo->metalink);
    lr_free(repo);
}


lr_store_mirrorlist_files(LrHandle *handle,
                          LrYumRepo *repo,
                          GError **err)
{
    int fd;
    int rc;

    if (handle->mirrorlist_fd != -1) {
        char *ml_file_path = lr_pathconcat(handle->destdir,
                                           "mirrorlist", NULL);
        fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd < 0) {
            g_debug("%s: Cannot create: %s", __func__, ml_file_path);
            g_set_error(err, LR_YUM_ERROR, LRE_IO,
                        "Cannot create %s: %s", ml_file_path, g_strerror(errno));
            lr_free(ml_file_path);
            return FALSE;
        }
        rc = lr_copy_content(handle->mirrorlist_fd, fd);
        close(fd);
        if (rc != 0) {
            g_debug("%s: Cannot copy content of mirrorlist file", __func__);
            g_set_error(err, LR_YUM_ERROR, LRE_IO,
                        "Cannot copy content of mirrorlist file %s: %s",
                        ml_file_path, g_strerror(errno));
            lr_free(ml_file_path);
            return FALSE;
        }
        repo->mirrorlist = ml_file_path;
    }

    return TRUE;
}

Validate path read from repomd.xml (RhBug:1868639)

= changelog =
msg: Validate path read from repomd.xml
type: security
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1868639

int fd;

char *expected_checksum;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_yum_download_url(LrHandle *lr_handle, const char *url, int fd,
                    gboolean no_cache, gboolean is_zchunk, GError **err)
{
    gboolean ret;
    LrDownloadTarget *target;
    GError *tmp_err = NULL;

    assert(url);
    assert(!err || *err == NULL);

    CbData *cbdata = cbdata_new(lr_handle->user_data,
                                NULL,
                                lr_handle->user_cb,
                                lr_handle->hmfcb,
                                url);

    // Prepare target
    target = lr_downloadtarget_new(lr_handle,
                                   url, NULL, fd, NULL,
                                   NULL, 0, 0,(lr_handle->user_cb) ? progresscb : NULL, cbdata,
                                   NULL, (lr_handle->hmfcb) ? hmfcb : NULL, NULL, 0, 0,
                                   NULL, no_cache, is_zchunk);

    // Download the target
    ret = lr_download_target(target, &tmp_err);

    assert(ret || tmp_err);
    assert(!(target->err) || !ret);
    cbdata_free(cbdata);

    if (!ret)
        g_propagate_error(err, tmp_err);

    lr_downloadtarget_free(target);

    lseek(fd, 0, SEEK_SET);

    return ret;
}

int fd;

char *expected_checksum;

gboolean ret, matches;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_yum_repo_path(LrYumRepo *repo, const char *type)
{
    assert(repo);

    gchar *chk_type = get_type(repo, type);
    const char *path = yum_repo_path(repo, chk_type);
    g_free(chk_type);
    return path;
}


lr_yum_perform(LrHandle *handle, LrResult *result, GError **err)
{
    int ret = TRUE;
    LrYumRepo *repo;
    LrYumRepoMd *repomd;

    assert(handle);
    assert(!err || *err == NULL);

    if (!result) {
        g_set_error(err, LR_YUM_ERROR, LRE_BADFUNCARG,
                    "Missing result parameter");
        return FALSE;
    }

    if (!handle->urls && !handle->mirrorlisturl && !handle->metalinkurl) {
        g_set_error(err, LR_YUM_ERROR, LRE_NOURL,
                "No LRO_URLS, LRO_MIRRORLISTURL nor LRO_METALINKURL specified");
        return FALSE;
    }

    if (handle->local && (!handle->urls || !handle->urls[0])) {
        g_set_error(err, LR_YUM_ERROR, LRE_NOURL,
                    "Localrepo specified, but no LRO_URLS set");
        return FALSE;
    }

    if (handle->update) {
        // Download/Locate only specified files
        if (!result->yum_repo || !result->yum_repomd) {
            g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETERESULT,
                    "Incomplete result object - "
                    "Cannot update on this result object");
            return FALSE;
        }
    } else {
        // Download/Locate from scratch
        if (result->yum_repo || result->yum_repomd) {
            g_set_error(err, LR_YUM_ERROR, LRE_ALREADYUSEDRESULT,
                        "This result object is not clear - "
                        "Already used result object");
            return FALSE;
        }
        result->yum_repo = lr_yum_repo_init();
        result->yum_repomd = lr_yum_repomd_init();
    }

    repo   = result->yum_repo;
    repomd = result->yum_repomd;

    if (handle->local) {
        // Do not duplicate repository, just use the existing local one

        ret = lr_yum_use_local(handle, result, err);
        if (!ret)
            return FALSE;

        if (handle->checks & LR_CHECK_CHECKSUM)
            ret = lr_yum_check_repo_checksums(repo, repomd, err);
    } else {
        // Download remote/Duplicate local repository
        // Note: All checksums are checked while downloading

        ret = lr_yum_download_remote(handle, result, err);
    }

    return ret;
}

int fd;

gboolean ret, matches;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_get_metadata_failure_callback(const LrHandle *handle)
{
    CbData *cbdata = NULL;
    if (handle->hmfcb) {
        cbdata = cbdata_new(handle->user_data,
                            NULL,
                            NULL,
                            handle->hmfcb,
                            "repomd.xml");
    }
    return cbdata;
}


lr_yum_repo_init(void)
{
    return lr_malloc0(sizeof(LrYumRepo));
}

int fd;

#define _DEFAULT_SOURCE

#define _POSIX_SOURCE

char *expected_checksum;

gboolean ret, matches;

#define  BITS_IN_BYTE 8

LrChecksumType checksum_type;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_yum_repomd_record_enabled(LrHandle *handle, const char *type, GSList* records)
{
    // Blacklist check
    if (handle->yumblist) {
        int x = 0;
        while (handle->yumblist[x]) {
            if (!strcmp(handle->yumblist[x], type))
                return FALSE;
            x++;
        }
    }

    // Whitelist check
    if (handle->yumdlist) {
        int x = 0;
        while (handle->yumdlist[x]) {
            if (!strcmp(handle->yumdlist[x], type))
                return TRUE;
            x++;
        }
        // Substitution check
        if (handle->yumslist) {
            for (GSList *elem = handle->yumslist; elem; elem = g_slist_next(elem)) {
                LrVar* subs = elem->data;
                if (!g_strcmp0(subs->val, type)) {
                    char *orig = subs->var;
                    for (guint i = 0; handle->yumdlist[i]; i++) {
                        if (!g_strcmp0(orig, handle->yumdlist[i]) &&
                            !g_slist_find_custom(records, orig, (GCompareFunc) compare_records))
                            return TRUE;
                    }
                    return FALSE;
                }
            }
        }
        return FALSE;
    }
    return TRUE;
}

int fd;

char *expected_checksum;

gboolean is_zchunk = FALSE;


lr_yum_download_repos(GSList *targets,
                      GError **err)
{
    gboolean ret;
    GSList *download_targets = NULL;
    GSList *cbdata_list = NULL;
    GError *download_error = NULL;

    for (GSList *elem = targets; elem; elem = g_slist_next(elem)) {
        LrMetadataTarget *target = elem->data;

        if (!target->handle) {
            continue;
        }

        prepare_repo_download_targets(target->handle,
                                      target->repo,
                                      target->repomd,
                                      target,
                                      &download_targets,
                                      &cbdata_list,
                                      &download_error);
    }

    if (!download_targets) {
        g_propagate_error(err, download_error);
        return TRUE;
    }

    ret = lr_download_single_cb(download_targets,
                                FALSE,
                                (cbdata_list) ? progresscb : NULL,
                                (cbdata_list) ? hmfcb : NULL,
                                &download_error);

    error_handling(download_targets, err, download_error);

    g_slist_free_full(cbdata_list, (GDestroyNotify)cbdata_free);
    g_slist_free_full(download_targets, (GDestroyNotify)lr_downloadtarget_free);

    return ret;
}

gboolean ret, matches;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


hmfcb(void *clientp, const char *msg, const char *url)
{
    CbData *data = clientp;
    if (data->hmfcb)
        return data->hmfcb(data->userdata, msg, url, data->metadata);
    return LR_CB_OK;
}


lr_yum_repo_append(LrYumRepo *repo, const char *type, const char *path)
{
    assert(repo);
    assert(type);
    assert(path);

    LrYumRepoPath *yumrepopath = lr_malloc(sizeof(LrYumRepoPath));
    yumrepopath->type = g_strdup(type);
    yumrepopath->path = g_strdup(path);
    repo->paths = g_slist_append(repo->paths, yumrepopath);
}


get_type(LrYumRepo *repo, const char *type)
{
    if (!repo->use_zchunk)
        return g_strdup(type);

    gchar *chk_type = g_strconcat(type, "_zck", NULL);

    for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
        LrYumRepoPath *yumrepopath = elem->data;
        assert(yumrepopath);
        if (!strcmp(yumrepopath->type, chk_type))
            return chk_type;
    }
    g_free(chk_type);
    return g_strdup(type);
}

char *expected_checksum;

GError *tmp_err = NULL;


lr_yum_repo_update(LrYumRepo *repo, const char *type, const char *path)
{
    assert(repo);
    assert(type);
    assert(path);

    for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
        LrYumRepoPath *yumrepopath = elem->data;
        assert(yumrepopath);

        if (!strcmp(yumrepopath->type, type)) {
            lr_free(yumrepopath->path);
            yumrepopath->path = g_strdup(path);
            return;
        }
    }

    lr_yum_repo_append(repo, type, path);
}


lr_copy_metalink_content(LrHandle *handle,
                         LrYumRepo *repo,
                         GError **err)
{
    int fd;
    int rc;

    if (handle->metalink_fd != -1) {
        char *ml_file_path = lr_pathconcat(handle->destdir,
                                           "metalink.xml", NULL);
        fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd < 0) {
            g_debug("%s: Cannot create: %s", __func__, ml_file_path);
            g_set_error(err, LR_YUM_ERROR, LRE_IO,
                        "Cannot create %s: %s", ml_file_path, g_strerror(errno));
            lr_free(ml_file_path);
            return FALSE;
        }
        rc = lr_copy_content(handle->metalink_fd, fd);
        close(fd);
        if (rc != 0) {
            g_debug("%s: Cannot copy content of metalink file", __func__);
            g_set_error(err, LR_YUM_ERROR, LRE_IO,
                        "Cannot copy content of metalink file %s: %s",
                        ml_file_path, g_strerror(errno));
            lr_free(ml_file_path);
            return FALSE;
        }
        repo->metalink = ml_file_path;
    }

    return TRUE;
}


lr_check_repomd_xml_asc_availability(LrHandle *handle,
                                     LrYumRepo *repo,
                                     int fd,
                                     char *path,
                                     GError **err)
{
    GError *tmp_err = NULL;
    gboolean ret;

    if (handle->checks & LR_CHECK_GPG) {
        int fd_sig;
        char *url, *signature;

        signature = lr_pathconcat(handle->destdir, "repodata/repomd.xml.asc", NULL);
        fd_sig = open(signature, O_CREAT | O_TRUNC | O_RDWR, 0666);
        if (fd_sig == -1) {
            g_debug("%s: Cannot open: %s", __func__, signature);
            g_set_error(err, LR_YUM_ERROR, LRE_IO,
                        "Cannot open %s: %s", signature, g_strerror(errno));
            lr_free(signature);
            return FALSE;
        }

        url = lr_pathconcat(handle->used_mirror, "repodata/repomd.xml.asc", NULL);
        ret = lr_download_url(handle, url, fd_sig, &tmp_err);
        lr_free(url);
        close(fd_sig);
        if (!ret) {
            // Error downloading signature
            g_set_error(err, LR_YUM_ERROR, LRE_BADGPG,
                        "GPG verification is enabled, but GPG signature "
                        "is not available. This may be an error or the "
                        "repository does not support GPG verification: %s", tmp_err->message);
            g_clear_error(&tmp_err);
            unlink(signature);
            lr_free(signature);
            return FALSE;
        } else {
            // Signature downloaded
            repo->signature = g_strdup(signature);
            ret = lr_gpg_check_signature(signature,
                                         path,
                                         handle->gnupghomedir,
                                         &tmp_err);
            lr_free(signature);
            if (!ret) {
                g_debug("%s: GPG signature verification failed: %s",
                        __func__, tmp_err->message);
                g_propagate_prefixed_error(err, tmp_err,
                                           "repomd.xml GPG signature verification error: ");
                return FALSE;
            }
            g_debug("%s: GPG signature successfully verified", __func__);
        }
    }

    return TRUE;
}


lr_yum_use_local(LrHandle *handle, LrResult *result, GError **err)
{
    char *baseurl;
    LrYumRepo *repo;
    LrYumRepoMd *repomd;

    assert(!err || *err == NULL);

    g_debug("%s: Locating repo..", __func__);

    // Shortcuts
    repo   = result->yum_repo;
    repomd = result->yum_repomd;
    baseurl = handle->urls[0];

    // Skip "file://" prefix if present
    if (g_str_has_prefix(baseurl, "file://"))
        baseurl += 7;
    else if (g_str_has_prefix(baseurl, "file:"))
        baseurl += 5;

    // Check sanity
    if (strstr(baseurl, "://")) {
        g_set_error(err, LR_YUM_ERROR, LRE_NOTLOCAL,
                    "URL: %s doesn't seem to be a local repository",
                    baseurl);
        return FALSE;
    }

    if (!handle->update) {
        // Load repomd.xml and mirrorlist+metalink if locally available
        if (!lr_yum_use_local_load_base(handle, result, repo, repomd, baseurl, err))
            return FALSE;
    }

    if(handle->cachedir) {
        lr_yum_switch_to_zchunk(handle, repomd);
        repo->use_zchunk = TRUE;
    } else {
        g_debug("%s: Cache directory not set, disabling zchunk", __func__);
        repo->use_zchunk = FALSE;
    }

    // Locate rest of metadata files
    for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
        _cleanup_free_ char *path = NULL;
        LrYumRepoMdRecord *record = elem->data;

        assert(record);

        if (!lr_yum_repomd_record_enabled(handle, record->type, repomd->records))
            continue; // Caller isn't interested in this record type
        if (yum_repo_path(repo, record->type))
            continue; // This path already exists in repo

        path = lr_pathconcat(baseurl, record->location_href, NULL);

        if (access(path, F_OK) == -1) {
            // A repo file is missing
            if (!handle->ignoremissing) {
                g_debug("%s: Incomplete repository - %s is missing",
                        __func__, path);
                g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETEREPO,
                            "Incomplete repository - %s is missing",
                            path);
                return FALSE;
            }

            continue;
        }

        lr_yum_repo_append(repo, record->type, path);
    }

    g_debug("%s: Repository was successfully located", __func__);
    return TRUE;
}

char *expected_checksum;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_yum_check_repo_checksums(LrYumRepo *repo,
                            LrYumRepoMd *repomd,
                            GError **err)
{
    assert(!err || *err == NULL);

    for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
        gboolean ret;
        LrYumRepoMdRecord *record = elem->data;

        assert(record);

        const char *path = yum_repo_path(repo, record->type);

        ret = lr_yum_check_checksum_of_md_record(record, path, err);
        if (!ret)
            return FALSE;
    }

    return TRUE;
}

char *expected_checksum;

gboolean ret, matches;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


lr_prepare_repodata_dir(LrHandle *handle,
                        GError **err)
{
    int rc;
    int create_repodata_dir = 1;
    char *path_to_repodata;

    path_to_repodata = lr_pathconcat(handle->destdir, "repodata", NULL);

    if (handle->update) {  /* Check if should create repodata/ subdir */
        struct stat buf;
        if (stat(path_to_repodata, &buf) != -1)
            if (S_ISDIR(buf.st_mode))
                create_repodata_dir = 0;
    }

    if (create_repodata_dir) {
        /* Prepare repodata/ subdir */
        rc = mkdir(path_to_repodata, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH);
        if (rc == -1) {
            g_debug("%s: Cannot create dir: %s (%s)",
                    __func__, path_to_repodata, g_strerror(errno));
            g_set_error(err, LR_YUM_ERROR, LRE_CANNOTCREATEDIR,
                        "Cannot create directory: %s: %s",
                        path_to_repodata, g_strerror(errno));
            lr_free(path_to_repodata);
            return FALSE;
        }
    }
    lr_free(path_to_repodata);

    return TRUE;
}


lr_get_best_checksum(const LrMetalink *metalink,
                     GSList **checksums)
{
    gboolean ret;
    LrChecksumType ch_type;
    gchar *ch_value;

    // From the metalink itself
    ret = lr_best_checksum(metalink->hashes, &ch_type, &ch_value);
    if (ret)
    {
        LrDownloadTargetChecksum *dtch;
        dtch = lr_downloadtargetchecksum_new(ch_type, ch_value);
        *checksums = g_slist_prepend(*checksums, dtch);
        g_debug("%s: Expected checksum for repomd.xml: (%s) %s",
                __func__, lr_checksum_type_to_str(ch_type), ch_value);
    }

    // From the alternates entries
    for (GSList *elem = metalink->alternates; elem; elem = g_slist_next(elem))
    {
        LrMetalinkAlternate *alt = elem->data;
        ret = lr_best_checksum(alt->hashes, &ch_type, &ch_value);
        if (ret) {
            LrDownloadTargetChecksum *dtch;
            dtch = lr_downloadtargetchecksum_new(ch_type, ch_value);
            *checksums = g_slist_prepend(*checksums, dtch);
            g_debug("%s: Expected alternate checksum for repomd.xml: (%s) %s",
                    __func__, lr_checksum_type_to_str(ch_type), ch_value);
        }
    }
}

LrChecksumType checksum_type;

gboolean ret, matches;

gboolean is_zchunk = FALSE;


lr_yum_switch_to_zchunk(LrHandle *handle, LrYumRepoMd *repomd)
{
    if (handle->yumdlist) {
        int x = 0;
        while (handle->yumdlist[x]) {
            char *check_type = g_strconcat(handle->yumdlist[x], "_zck", NULL);
            assert(check_type);

            /* Check whether we already want the zchunk version of this record */
            int found = FALSE;
            int y = 0;
            while (handle->yumdlist[y]) {
                if (y == x) {
                    y++;
                    continue;
                }
                if (strcmp(handle->yumdlist[y], check_type) == 0) {
                    found = TRUE;
                    break;
                }
                y++;
            }
            if (found) {
                g_free(check_type);
                x++;
                continue;
            }

            found = FALSE;
            /* Check whether the zchunk version of this record exists */
            for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
                LrYumRepoMdRecord *record = elem->data;

                if (strcmp(record->type, check_type) == 0) {
                    g_debug("Found %s so using instead of %s", check_type,
                            handle->yumdlist[x]);
                    g_free(handle->yumdlist[x]);
                    handle->yumdlist[x] = check_type;
                    found = TRUE;
                    break;
                }
            }
            if (!found)
                g_free(check_type);
            x++;
        }
    }
    return;
}


lr_prepare_repomd_xml_file(LrHandle *handle,
                           char **path,
                           GError **err)
{
    int fd;

    *path = lr_pathconcat(handle->destdir, "/repodata/repomd.xml", NULL);
    fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666);
    if (fd == -1) {
        g_set_error(err, LR_YUM_ERROR, LRE_IO,
                    "Cannot open %s: %s", *path, g_strerror(errno));
        lr_free(*path);
        return -1;
    }

    return fd;
}

int fd;

char *expected_checksum;

GError *tmp_err = NULL;


prepare_repo_download_zck_target(LrHandle *handle,
                                 LrYumRepoMdRecord *record,
                                 char **path,
                                 int *fd,
                                 GSList **checksums,
                                 GSList **targets,
                                 GError **err)
{
    *path = lr_pathconcat(handle->destdir, record->location_href, NULL);
    *fd = open(*path, O_CREAT|O_RDWR, 0666);
    if (*fd < 0) {
        g_debug("%s: Cannot create/open %s (%s)",
                __func__, *path, g_strerror(errno));
        g_set_error(err, LR_YUM_ERROR, LRE_IO,
                    "Cannot create/open %s: %s", *path, g_strerror(errno));
        lr_free(*path);
        g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free);
        return FALSE;
    }

    if (handle->checks & LR_CHECK_CHECKSUM) {
        // Select proper checksum type only if checksum check is enabled
        LrDownloadTargetChecksum *checksum;
        checksum = lr_downloadtargetchecksum_new(
                       lr_checksum_type(record->header_checksum_type),
                       record->header_checksum);
        *checksums = g_slist_prepend(*checksums, checksum);
    }
    return TRUE;
}


cbdata_free(CbData *data)
{
    if (!data) return;
    free(data->metadata);
    free(data);
}


lr_yum_download_repomd(LrHandle *handle,
                       LrMetalink *metalink,
                       int fd,
                       GError **err)
{
    int ret = TRUE;
    GError *tmp_err = NULL;

    assert(!err || *err == NULL);

    g_debug("%s: Downloading repomd.xml via mirrorlist", __func__);

    GSList *checksums = NULL;
    if (metalink && (handle->checks & LR_CHECK_CHECKSUM)) {
        lr_get_best_checksum(metalink, &checksums);
    }

    CbData *cbdata = cbdata_new(handle->user_data,
                            NULL,
                            handle->user_cb,
                            handle->hmfcb,
                            "repomd.xml");

    LrDownloadTarget *target = lr_downloadtarget_new(handle,
                                                     "repodata/repomd.xml",
                                                     NULL,
                                                     fd,
                                                     NULL,
                                                     checksums,
                                                     0,
                                                     0,
                                                     (handle->user_cb) ? progresscb : NULL,
                                                     cbdata,
                                                     NULL,
                                                     (handle->hmfcb) ? hmfcb : NULL,
                                                     NULL,
                                                     0,
                                                     0,
                                                     NULL,
                                                     TRUE,
                                                     FALSE);

    ret = lr_download_target(target, &tmp_err);
    assert((ret && !tmp_err) || (!ret && tmp_err));

    if (cbdata)
        cbdata_free(cbdata);

    if (tmp_err) {
        g_propagate_prefixed_error(err, tmp_err,
                                   "Cannot download repomd.xml: ");
    } else if (target->err) {
        assert(0); // This should not happen since failfast should be TRUE
        ret = FALSE;
        g_set_error(err, LR_DOWNLOADER_ERROR, target->rcode,
                    "Cannot download repomd.xml: %s",target->err);
    } else {
        // Set mirror used for download a repomd.xml to the handle
        // TODO: Get rid of use_mirror attr
        lr_free(handle->used_mirror);
        handle->used_mirror = g_strdup(target->usedmirror);
    }

    lr_downloadtarget_free(target);

    if (!ret) {
        /* Download of repomd.xml was not successful */
        g_debug("%s: repomd.xml download was unsuccessful", __func__);
    }

    return ret;
}


compare_records(gconstpointer a, gconstpointer b)
{
    LrYumRepoMdRecord* yum_record = (LrYumRepoMdRecord*) a;
    char *type1 = (char *) yum_record->type;
    char *type2 = (char *) b;
    return g_strcmp0(type1, type2);
}


lr_yum_download_remote(LrHandle *handle, LrResult *result, GError **err)
{
    gboolean ret = TRUE;
    int fd;
    LrYumRepo *repo;
    LrYumRepoMd *repomd;
    GError *tmp_err = NULL;

    assert(!err || *err == NULL);

    repo   = result->yum_repo;
    repomd = result->yum_repomd;

    g_debug("%s: Downloading/Copying repo..", __func__);

    if (!lr_prepare_repodata_dir(handle, err))
        return FALSE;

    if (!handle->update) {
        char *path = NULL;

        if (!lr_store_mirrorlist_files(handle, repo, err))
            return FALSE;

        if (!lr_copy_metalink_content(handle, repo, err))
            return FALSE;

        if ((fd = lr_prepare_repomd_xml_file(handle, &path, err)) == -1)
            return FALSE;

        /* Download repomd.xml */
        ret = lr_yum_download_repomd(handle, handle->metalink, fd, err);
        if (!ret) {
            close(fd);
            lr_free(path);
            return FALSE;
        }

        if (!lr_check_repomd_xml_asc_availability(handle, repo, fd, path, err)) {
            close(fd);
            lr_free(path);
            return FALSE;
        }

        lseek(fd, 0, SEEK_SET);

        /* Parse repomd */
        g_debug("%s: Parsing repomd.xml", __func__);
        ret = lr_yum_repomd_parse_file(repomd, fd, lr_xml_parser_warning_logger,
                                       "Repomd xml parser", &tmp_err);
        close(fd);
        if (!ret) {
            g_debug("%s: Parsing unsuccessful: %s", __func__, tmp_err->message);
            g_propagate_prefixed_error(err, tmp_err,
                                       "repomd.xml parser error: ");
            lr_free(path);
            return FALSE;
        }

        /* Fill result object */
        result->destdir = g_strdup(handle->destdir);
        repo->destdir = g_strdup(handle->destdir);
        repo->repomd = path;
        if (handle->used_mirror)
            repo->url = g_strdup(handle->used_mirror);
        else
            repo->url = g_strdup(handle->urls[0]);

        g_debug("%s: Repomd revision: %s", repomd->revision, __func__);
    }

    /* Download rest of metadata files */
    ret = lr_yum_download_repo(handle, repo, repomd, &tmp_err);
    assert((ret && !tmp_err) || (!ret && tmp_err));

    if (!ret) {
        g_debug("%s: Repository download error: %s", __func__, tmp_err->message);
        g_propagate_prefixed_error(err, tmp_err, "Yum repo downloading error: ");
        return FALSE;
    }

    return TRUE;
}


prepare_repo_download_std_target(LrHandle *handle,
                                 LrYumRepoMdRecord *record,
                                 char **path,
                                 int *fd,
                                 GSList **checksums,
                                 GSList **targets,
                                 GError **err)
{
    *path = lr_pathconcat(handle->destdir, record->location_href, NULL);
    *fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666);
    if (*fd < 0) {
        g_debug("%s: Cannot create/open %s (%s)",
                __func__, *path, g_strerror(errno));
        g_set_error(err, LR_YUM_ERROR, LRE_IO,
                    "Cannot create/open %s: %s", *path, g_strerror(errno));
        lr_free(*path);
        g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free);
        return FALSE;
    }

    if (handle->checks & LR_CHECK_CHECKSUM) {
        // Select proper checksum type only if checksum check is enabled
        LrDownloadTargetChecksum *checksum;
        checksum = lr_downloadtargetchecksum_new(
                       lr_checksum_type(record->checksum_type),
                       record->checksum);
        *checksums = g_slist_prepend(*checksums, checksum);
    }
    return TRUE;
}

int fd;

char *expected_checksum;

LrChecksumType checksum_type;

gboolean is_zchunk = FALSE;

GError *tmp_err = NULL;


static int jpc_dec_tilefini(jpc_dec_t *dec, jpc_dec_tile_t *tile)
{
	jpc_dec_tcomp_t *tcomp;
	int compno;
	int bandno;
	unsigned rlvlno;
	jpc_dec_band_t *band;
	jpc_dec_rlvl_t *rlvl;
	int prcno;
	jpc_dec_prc_t *prc;
	jpc_dec_seg_t *seg;
	jpc_dec_cblk_t *cblk;
	int cblkno;

	if (tile->tcomps) {

		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
		  ++compno, ++tcomp) {
			for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
			  ++rlvlno, ++rlvl) {
				if (!rlvl->bands) {
					continue;
				}
				for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
				  ++bandno, ++band) {
					if (band->prcs) {
						for (prcno = 0, prc = band->prcs; prcno <
						  rlvl->numprcs; ++prcno, ++prc) {
							if (!prc->cblks) {
								continue;
							}
							for (cblkno = 0, cblk = prc->cblks; cblkno <
							  prc->numcblks; ++cblkno, ++cblk) {

								while (cblk->segs.head) {
									seg = cblk->segs.head;
									jpc_seglist_remove(&cblk->segs, seg);
									jpc_seg_destroy(seg);
								}
								jas_matrix_destroy(cblk->data);
								if (cblk->mqdec) {
									jpc_mqdec_destroy(cblk->mqdec);
								}
								if (cblk->nulldec) {
									jpc_bitstream_close(cblk->nulldec);
								}
								if (cblk->flags) {
									jas_matrix_destroy(cblk->flags);
								}
							}
							if (prc->incltagtree) {
								jpc_tagtree_destroy(prc->incltagtree);
							}
							if (prc->numimsbstagtree) {
								jpc_tagtree_destroy(prc->numimsbstagtree);
							}
							if (prc->cblks) {
								jas_free(prc->cblks);
							}
						}
					}
					if (band->data) {
						jas_matrix_destroy(band->data);
					}
					if (band->prcs) {
						jas_free(band->prcs);
					}
				}
				if (rlvl->bands) {
					jas_free(rlvl->bands);
				}
			}
			if (tcomp->rlvls) {
				jas_free(tcomp->rlvls);
			}
			if (tcomp->data) {
				jas_matrix_destroy(tcomp->data);
			}
			if (tcomp->tsfb) {
				jpc_tsfb_destroy(tcomp->tsfb);
			}
		}
	}

	if (tile->cp) {
		jpc_dec_cp_destroy(tile->cp);
		//tile->cp = 0;
	}
	if (tile->tcomps) {
		jas_free(tile->tcomps);
		//tile->tcomps = 0;
	}
	if (tile->pi) {
		jpc_pi_destroy(tile->pi);
		//tile->pi = 0;
	}
	if (tile->pkthdrstream) {
		jas_stream_close(tile->pkthdrstream);
		//tile->pkthdrstream = 0;
	}
	if (tile->pptstab) {
		jpc_ppxstab_destroy(tile->pptstab);
		//tile->pptstab = 0;
	}

	tile->state = JPC_TILE_DONE;

	return 0;
}

jpc_dec: fix another integer overflow in SIZ

Validate the width and height values first, before doing anything
else.  This prevents integer overflows in the `numhtiles` /
`numvtiles` calculation below, triggering assertion failures.

Fixes CVE-2017-13750

Closes https://github.com/mdadams/jasper/issues/165
Closes https://github.com/mdadams/jasper/issues/174

typedef struct {

	uint_fast16_t id;
	/* The marker segment type. */

	int validstates;
	/* The states in which this type of marker segment can be
	  validly encountered. */

	int (*action)(jpc_dec_t *dec, jpc_ms_t *ms);
	/* The action to take upon encountering this type of marker segment. */

} jpc_dec_mstabent_t;

static int jpc_dec_dump(jpc_dec_t *dec, FILE *out);

static jpc_ppxstab_t *jpc_ppxstab_create(void);

static void jpc_ppxstab_destroy(jpc_ppxstab_t *tab);

static int jpc_ppxstab_grow(jpc_ppxstab_t *tab, int maxents);

static int jpc_ppxstab_insert(jpc_ppxstab_t *tab, jpc_ppxstabent_t *ent);

static jpc_streamlist_t *jpc_ppmstabtostreams(jpc_ppxstab_t *tab);

static int jpc_pptstabwrite(jas_stream_t *out, jpc_ppxstab_t *tab);

static jpc_ppxstabent_t *jpc_ppxstabent_create(void);

static void jpc_ppxstabent_destroy(jpc_ppxstabent_t *ent);

static int jpc_streamlist_numstreams(jpc_streamlist_t *streamlist);

static jpc_streamlist_t *jpc_streamlist_create(void);

static int jpc_streamlist_insert(jpc_streamlist_t *streamlist, int streamno,
  jas_stream_t *stream);

static jas_stream_t *jpc_streamlist_remove(jpc_streamlist_t *streamlist, int streamno);

static void jpc_streamlist_destroy(jpc_streamlist_t *streamlist);

static void jpc_dec_cp_resetflags(jpc_dec_cp_t *cp);

static jpc_dec_cp_t *jpc_dec_cp_create(uint_fast16_t numcomps);

static int jpc_dec_cp_isvalid(jpc_dec_cp_t *cp);

static jpc_dec_cp_t *jpc_dec_cp_copy(jpc_dec_cp_t *cp);

static int jpc_dec_cp_setfromcod(jpc_dec_cp_t *cp, jpc_cod_t *cod);

static int jpc_dec_cp_setfromcoc(jpc_dec_cp_t *cp, jpc_coc_t *coc);

static int jpc_dec_cp_setfromcox(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
  jpc_coxcp_t *compparms, int flags);

static int jpc_dec_cp_setfromqcd(jpc_dec_cp_t *cp, jpc_qcd_t *qcd);

static int jpc_dec_cp_setfromqcc(jpc_dec_cp_t *cp, jpc_qcc_t *qcc);

static int jpc_dec_cp_setfromqcx(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
  jpc_qcxcp_t *compparms, int flags);

static int jpc_dec_cp_setfromrgn(jpc_dec_cp_t *cp, jpc_rgn_t *rgn);

static int jpc_dec_cp_prepare(jpc_dec_cp_t *cp);

static void jpc_dec_cp_destroy(jpc_dec_cp_t *cp);

static int jpc_dec_cp_setfrompoc(jpc_dec_cp_t *cp, jpc_poc_t *poc, int reset);

static int jpc_pi_addpchgfrompoc(jpc_pi_t *pi, jpc_poc_t *poc);

static int jpc_dec_decode(jpc_dec_t *dec);

static jpc_dec_t *jpc_dec_create(jpc_dec_importopts_t *impopts,
  jas_stream_t *in);

static void jpc_dec_destroy(jpc_dec_t *dec);


static int jpc_dec_tiledecode(jpc_dec_t *dec, jpc_dec_tile_t *tile)
{
	int i;
	int j;
	jpc_dec_tcomp_t *tcomp;
	jpc_dec_rlvl_t *rlvl;
	jpc_dec_band_t *band;
	int compno;
	unsigned rlvlno;
	int bandno;
	int adjust;
	int v;
	jpc_dec_ccp_t *ccp;
	jpc_dec_cmpt_t *cmpt;

	if (jpc_dec_decodecblks(dec, tile)) {
		jas_eprintf("jpc_dec_decodecblks failed\n");
		return -1;
	}

	/* Perform dequantization. */
	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
	  ++compno, ++tcomp) {
		ccp = &tile->cp->ccps[compno];
		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
		  ++rlvlno, ++rlvl) {
			if (!rlvl->bands) {
				continue;
			}
			for (bandno = 0, band = rlvl->bands;
			  bandno < rlvl->numbands; ++bandno, ++band) {
				if (!band->data) {
					continue;
				}
				jpc_undo_roi(band->data, band->roishift, ccp->roishift -
				  band->roishift, band->numbps);
				if (tile->realmode) {
					jas_matrix_asl(band->data, JPC_FIX_FRACBITS);
					jpc_dequantize(band->data, band->absstepsize);
				}

			}
		}
	}

	/* Apply an inverse wavelet transform if necessary. */
	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
	  ++compno, ++tcomp) {
		ccp = &tile->cp->ccps[compno];
		jpc_tsfb_synthesize(tcomp->tsfb, tcomp->data);
	}

	/* Apply an inverse intercomponent transform if necessary. */
	switch (tile->cp->mctid) {
	case JPC_MCT_RCT:
		if (dec->numcomps < 3) {
			jas_eprintf("RCT requires at least three components\n");
			return -1;
		}
		if (!jas_image_cmpt_domains_same(dec->image)) {
			jas_eprintf("RCT requires all components have the same domain\n");
			return -1;
		}
		jpc_irct(tile->tcomps[0].data, tile->tcomps[1].data,
		  tile->tcomps[2].data);
		break;
	case JPC_MCT_ICT:
		if (dec->numcomps < 3) {
			jas_eprintf("ICT requires at least three components\n");
			return -1;
		}
		if (!jas_image_cmpt_domains_same(dec->image)) {
			jas_eprintf("RCT requires all components have the same domain\n");
			return -1;
		}
		jpc_iict(tile->tcomps[0].data, tile->tcomps[1].data,
		  tile->tcomps[2].data);
		break;
	}

	/* Perform rounding and convert to integer values. */
	if (tile->realmode) {
		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
		  ++compno, ++tcomp) {
			for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
				for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
					v = jas_matrix_get(tcomp->data, i, j);
					v = jpc_fix_round(v);
					jas_matrix_set(tcomp->data, i, j, jpc_fixtoint(v));
				}
			}
		}
	}

	/* Perform level shift. */
	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
		adjust = cmpt->sgnd ? 0 : (1 << (cmpt->prec - 1));
		for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
			for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
				*jas_matrix_getref(tcomp->data, i, j) += adjust;
			}
		}
	}

	/* Perform clipping. */
	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
		jpc_fix_t mn;
		jpc_fix_t mx;
		mn = cmpt->sgnd ? (-(1 << (cmpt->prec - 1))) : (0);
		mx = cmpt->sgnd ? ((1 << (cmpt->prec - 1)) - 1) : ((1 <<
		  cmpt->prec) - 1);
		jas_matrix_clip(tcomp->data, mn, mx);
	}

	/* XXX need to free tsfb struct */

	/* Write the data for each component of the image. */
	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
		if (jas_image_writecmpt(dec->image, compno, tcomp->xstart -
		  JPC_CEILDIV(dec->xstart, cmpt->hstep), tcomp->ystart -
		  JPC_CEILDIV(dec->ystart, cmpt->vstep), jas_matrix_numcols(
		  tcomp->data), jas_matrix_numrows(tcomp->data), tcomp->data)) {
			jas_eprintf("write component failed\n");
			return -1;
		}
	}

	return 0;
}


static void jpc_dec_destroy(jpc_dec_t *dec)
{
	if (dec->cstate) {
		jpc_cstate_destroy(dec->cstate);
	}
	if (dec->pkthdrstreams) {
		jpc_streamlist_destroy(dec->pkthdrstreams);
	}
	if (dec->ppmstab) {
		jpc_ppxstab_destroy(dec->ppmstab);
	}
	if (dec->image) {
		jas_image_destroy(dec->image);
	}

	if (dec->cp) {
		jpc_dec_cp_destroy(dec->cp);
	}

	if (dec->cmpts) {
		jas_free(dec->cmpts);
	}

	if (dec->tiles) {
		int tileno;
		jpc_dec_tile_t *tile;

		for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
		  ++tile) {
			if (tile->state != JPC_TILE_DONE) {
				jpc_dec_tilefini(dec, tile);
			}
		}

		jas_free(dec->tiles);
	}

	jas_free(dec);
}


static int jpc_dec_tileinit(jpc_dec_t *dec, jpc_dec_tile_t *tile)
{
	jpc_dec_tcomp_t *tcomp;
	int compno;
	unsigned rlvlno;
	jpc_dec_rlvl_t *rlvl;
	jpc_dec_band_t *band;
	jpc_dec_prc_t *prc;
	int bndno;
	jpc_tsfb_band_t *bnd;
	int bandno;
	jpc_dec_ccp_t *ccp;
	int prccnt;
	jpc_dec_cblk_t *cblk;
	int cblkcnt;
	uint_fast32_t tlprcxstart;
	uint_fast32_t tlprcystart;
	uint_fast32_t brprcxend;
	uint_fast32_t brprcyend;
	uint_fast32_t tlcbgxstart;
	uint_fast32_t tlcbgystart;
	uint_fast32_t brcbgxend;
	uint_fast32_t brcbgyend;
	uint_fast32_t cbgxstart;
	uint_fast32_t cbgystart;
	uint_fast32_t cbgxend;
	uint_fast32_t cbgyend;
	uint_fast32_t tlcblkxstart;
	uint_fast32_t tlcblkystart;
	uint_fast32_t brcblkxend;
	uint_fast32_t brcblkyend;
	uint_fast32_t cblkxstart;
	uint_fast32_t cblkystart;
	uint_fast32_t cblkxend;
	uint_fast32_t cblkyend;
	uint_fast32_t tmpxstart;
	uint_fast32_t tmpystart;
	uint_fast32_t tmpxend;
	uint_fast32_t tmpyend;
	jpc_dec_cp_t *cp;
	jpc_tsfb_band_t bnds[JPC_MAXBANDS];
	jpc_pchg_t *pchg;
	int pchgno;
	jpc_dec_cmpt_t *cmpt;

	cp = tile->cp;
	tile->realmode = 0;
	if (cp->mctid == JPC_MCT_ICT) {
		tile->realmode = 1;
	}

	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
		ccp = &tile->cp->ccps[compno];
		if (ccp->qmfbid == JPC_COX_INS) {
			tile->realmode = 1;
		}
		tcomp->numrlvls = ccp->numrlvls;
		if (!(tcomp->rlvls = jas_alloc2(tcomp->numrlvls,
		  sizeof(jpc_dec_rlvl_t)))) {
			return -1;
		}
		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
		  ++rlvlno, ++rlvl) {
			rlvl->bands = NULL;
		}
		if (!(tcomp->data = jas_seq2d_create(JPC_CEILDIV(tile->xstart,
		  cmpt->hstep), JPC_CEILDIV(tile->ystart, cmpt->vstep),
		  JPC_CEILDIV(tile->xend, cmpt->hstep), JPC_CEILDIV(tile->yend,
		  cmpt->vstep)))) {
			return -1;
		}
		if (!(tcomp->tsfb = jpc_cod_gettsfb(ccp->qmfbid,
		  tcomp->numrlvls - 1))) {
			return -1;
		}
		{
			jpc_tsfb_getbands(tcomp->tsfb, jas_seq2d_xstart(tcomp->data),
			  jas_seq2d_ystart(tcomp->data), jas_seq2d_xend(tcomp->data),
			  jas_seq2d_yend(tcomp->data), bnds);
		}
		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
		  ++rlvlno, ++rlvl) {
			rlvl->bands = 0;
			rlvl->xstart = JPC_CEILDIVPOW2(tcomp->xstart,
			  tcomp->numrlvls - 1 - rlvlno);
			rlvl->ystart = JPC_CEILDIVPOW2(tcomp->ystart,
			  tcomp->numrlvls - 1 - rlvlno);
			rlvl->xend = JPC_CEILDIVPOW2(tcomp->xend,
			  tcomp->numrlvls - 1 - rlvlno);
			rlvl->yend = JPC_CEILDIVPOW2(tcomp->yend,
			  tcomp->numrlvls - 1 - rlvlno);
			rlvl->prcwidthexpn = ccp->prcwidthexpns[rlvlno];
			rlvl->prcheightexpn = ccp->prcheightexpns[rlvlno];
			tlprcxstart = JPC_FLOORDIVPOW2(rlvl->xstart,
			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
			tlprcystart = JPC_FLOORDIVPOW2(rlvl->ystart,
			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
			brprcxend = JPC_CEILDIVPOW2(rlvl->xend,
			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
			brprcyend = JPC_CEILDIVPOW2(rlvl->yend,
			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
			rlvl->numhprcs = (brprcxend - tlprcxstart) >>
			  rlvl->prcwidthexpn;
			rlvl->numvprcs = (brprcyend - tlprcystart) >>
			  rlvl->prcheightexpn;
			rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;

			if (rlvl->numprcs >= 64 * 1024) {
				/* avoid out-of-memory due to
				   malicious file; this limit is
				   rather arbitrary; "good" files I
				   have seen have values 1..12 */
				return -1;
			}

			if (rlvl->xstart >= rlvl->xend || rlvl->ystart >= rlvl->yend) {
				rlvl->bands = 0;
				rlvl->numprcs = 0;
				rlvl->numhprcs = 0;
				rlvl->numvprcs = 0;
				continue;
			}
			if (!rlvlno) {
				tlcbgxstart = tlprcxstart;
				tlcbgystart = tlprcystart;
				brcbgxend = brprcxend;
				brcbgyend = brprcyend;
				rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
				rlvl->cbgheightexpn = rlvl->prcheightexpn;
			} else {
				tlcbgxstart = JPC_CEILDIVPOW2(tlprcxstart, 1);
				tlcbgystart = JPC_CEILDIVPOW2(tlprcystart, 1);
				brcbgxend = JPC_CEILDIVPOW2(brprcxend, 1);
				brcbgyend = JPC_CEILDIVPOW2(brprcyend, 1);
				rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
				rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
				if (rlvl->cbgwidthexpn < 0 || rlvl->cbgheightexpn < 0) {
					/* the control block width/height offset
					   exponent must not be negative */
					return -1;
				}
			}
			rlvl->cblkwidthexpn = JAS_MIN(ccp->cblkwidthexpn,
			  rlvl->cbgwidthexpn);
			rlvl->cblkheightexpn = JAS_MIN(ccp->cblkheightexpn,
			  rlvl->cbgheightexpn);

			rlvl->numbands = (!rlvlno) ? 1 : 3;
			if (!(rlvl->bands = jas_alloc2(rlvl->numbands,
			  sizeof(jpc_dec_band_t)))) {
				return -1;
			}
			for (bandno = 0, band = rlvl->bands;
			  bandno < rlvl->numbands; ++bandno, ++band) {
				bndno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) +
				  bandno + 1);
				bnd = &bnds[bndno];

				band->orient = bnd->orient;
				band->stepsize = ccp->stepsizes[bndno];
				band->analgain = JPC_NOMINALGAIN(ccp->qmfbid,
				  tcomp->numrlvls - 1, rlvlno, band->orient);
				band->absstepsize = jpc_calcabsstepsize(band->stepsize,
				  cmpt->prec + band->analgain);
				band->numbps = ccp->numguardbits +
				  JPC_QCX_GETEXPN(band->stepsize) - 1;
				band->roishift = (ccp->roishift + band->numbps >= JPC_PREC) ?
				  (JPC_PREC - 1 - band->numbps) : ccp->roishift;
				band->data = 0;
				band->prcs = 0;
				if (bnd->xstart == bnd->xend || bnd->ystart == bnd->yend) {
					continue;
				}
				if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
					return -1;
				}
				if (jas_seq2d_bindsub(band->data, tcomp->data, bnd->locxstart,
						      bnd->locystart, bnd->locxend, bnd->locyend)) {
					return -1;
				}
				jas_seq2d_setshift(band->data, bnd->xstart, bnd->ystart);

				assert(rlvl->numprcs);

				if (!(band->prcs = jas_alloc2(rlvl->numprcs,
				  sizeof(jpc_dec_prc_t)))) {
					return -1;
				}

/************************************************/
				cbgxstart = tlcbgxstart;
				cbgystart = tlcbgystart;
				for (prccnt = rlvl->numprcs, prc = band->prcs;
				  prccnt > 0; --prccnt, ++prc) {
					cbgxend = cbgxstart + (1 << rlvl->cbgwidthexpn);
					cbgyend = cbgystart + (1 << rlvl->cbgheightexpn);
					prc->xstart = JAS_MAX(cbgxstart, JAS_CAST(uint_fast32_t,
					  jas_seq2d_xstart(band->data)));
					prc->ystart = JAS_MAX(cbgystart, JAS_CAST(uint_fast32_t,
					  jas_seq2d_ystart(band->data)));
					prc->xend = JAS_MIN(cbgxend, JAS_CAST(uint_fast32_t,
					  jas_seq2d_xend(band->data)));
					prc->yend = JAS_MIN(cbgyend, JAS_CAST(uint_fast32_t,
					  jas_seq2d_yend(band->data)));
					if (prc->xend > prc->xstart && prc->yend > prc->ystart) {
						tlcblkxstart = JPC_FLOORDIVPOW2(prc->xstart,
						  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
						tlcblkystart = JPC_FLOORDIVPOW2(prc->ystart,
						  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
						brcblkxend = JPC_CEILDIVPOW2(prc->xend,
						  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
						brcblkyend = JPC_CEILDIVPOW2(prc->yend,
						  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
						prc->numhcblks = (brcblkxend - tlcblkxstart) >>
						  rlvl->cblkwidthexpn;
						prc->numvcblks = (brcblkyend - tlcblkystart) >>
						  rlvl->cblkheightexpn;
						prc->numcblks = prc->numhcblks * prc->numvcblks;
						assert(prc->numcblks > 0);

						if (!(prc->incltagtree = jpc_tagtree_create(
						  prc->numhcblks, prc->numvcblks))) {
							return -1;
						}
						if (!(prc->numimsbstagtree = jpc_tagtree_create(
						  prc->numhcblks, prc->numvcblks))) {
							return -1;
						}
						if (!(prc->cblks = jas_alloc2(prc->numcblks,
						  sizeof(jpc_dec_cblk_t)))) {
							return -1;
						}

						cblkxstart = cbgxstart;
						cblkystart = cbgystart;
						for (cblkcnt = prc->numcblks, cblk = prc->cblks;
						  cblkcnt > 0;) {
							cblkxend = cblkxstart + (1 << rlvl->cblkwidthexpn);
							cblkyend = cblkystart + (1 << rlvl->cblkheightexpn);
							tmpxstart = JAS_MAX(cblkxstart, prc->xstart);
							tmpystart = JAS_MAX(cblkystart, prc->ystart);
							tmpxend = JAS_MIN(cblkxend, prc->xend);
							tmpyend = JAS_MIN(cblkyend, prc->yend);
							if (tmpxend > tmpxstart && tmpyend > tmpystart) {
								cblk->firstpassno = -1;
								cblk->mqdec = 0;
								cblk->nulldec = 0;
								cblk->flags = 0;
								cblk->numpasses = 0;
								cblk->segs.head = 0;
								cblk->segs.tail = 0;
								cblk->curseg = 0;
								cblk->numimsbs = 0;
								cblk->numlenbits = 3;
								cblk->flags = 0;
								if (!(cblk->data = jas_seq2d_create(0, 0, 0,
								  0))) {
									return -1;
								}
								if (jas_seq2d_bindsub(cblk->data, band->data,
										      tmpxstart, tmpystart, tmpxend, tmpyend)) {
									return -1;
								}
								++cblk;
								--cblkcnt;
							}
							cblkxstart += 1 << rlvl->cblkwidthexpn;
							if (cblkxstart >= cbgxend) {
								cblkxstart = cbgxstart;
								cblkystart += 1 << rlvl->cblkheightexpn;
							}
						}

					} else {
						prc->cblks = 0;
						prc->incltagtree = 0;
						prc->numimsbstagtree = 0;
					}
					cbgxstart += 1 << rlvl->cbgwidthexpn;
					if (cbgxstart >= brcbgxend) {
						cbgxstart = tlcbgxstart;
						cbgystart += 1 << rlvl->cbgheightexpn;
					}

				}
/********************************************/
			}
		}
	}

	if (!(tile->pi = jpc_dec_pi_create(dec, tile))) {
		return -1;
	}

	for (pchgno = 0; pchgno < jpc_pchglist_numpchgs(tile->cp->pchglist);
	  ++pchgno) {
		pchg = jpc_pchg_copy(jpc_pchglist_get(tile->cp->pchglist, pchgno));
		assert(pchg);
		jpc_pi_addpchg(tile->pi, pchg);
	}
	jpc_pi_init(tile->pi);

	return 0;
}


static int jpc_dec_process_siz(jpc_dec_t *dec, jpc_ms_t *ms)
{
	jpc_siz_t *siz = &ms->parms.siz;
	int compno;
	int tileno;
	jpc_dec_tile_t *tile;
	jpc_dec_tcomp_t *tcomp;
	int htileno;
	int vtileno;
	jpc_dec_cmpt_t *cmpt;
	size_t size;
	size_t num_samples;
	size_t num_samples_delta;

	size_t total_samples;
	if (!jas_safe_size_mul(siz->width, siz->height, &total_samples) ||
	    (dec->max_samples > 0 && total_samples > dec->max_samples)) {
		jas_eprintf("image too large\n");
		return -1;
	}

	size_t tile_samples;
	if (!jas_safe_size_mul(siz->tilewidth, siz->tileheight, &tile_samples) ||
	    (dec->max_samples > 0 && tile_samples > dec->max_samples)) {
		jas_eprintf("tile too large\n");
		return -1;
	}

	dec->xstart = siz->xoff;
	dec->ystart = siz->yoff;
	dec->xend = siz->width;
	dec->yend = siz->height;
	dec->tilewidth = siz->tilewidth;
	dec->tileheight = siz->tileheight;
	dec->tilexoff = siz->tilexoff;
	dec->tileyoff = siz->tileyoff;
	dec->numcomps = siz->numcomps;

	if (!(dec->cp = jpc_dec_cp_create(dec->numcomps))) {
		return -1;
	}

	if (!(dec->cmpts = jas_alloc2(dec->numcomps, sizeof(jpc_dec_cmpt_t)))) {
		return -1;
	}

	num_samples = 0;
	for (compno = 0, cmpt = dec->cmpts; compno < dec->numcomps; ++compno,
	  ++cmpt) {
		cmpt->prec = siz->comps[compno].prec;
		cmpt->sgnd = siz->comps[compno].sgnd;
		cmpt->hstep = siz->comps[compno].hsamp;
		cmpt->vstep = siz->comps[compno].vsamp;
		cmpt->width = JPC_CEILDIV(dec->xend, cmpt->hstep) -
		  JPC_CEILDIV(dec->xstart, cmpt->hstep);
		cmpt->height = JPC_CEILDIV(dec->yend, cmpt->vstep) -
		  JPC_CEILDIV(dec->ystart, cmpt->vstep);
		cmpt->hsubstep = 0;
		cmpt->vsubstep = 0;

		if (!jas_safe_size_mul(cmpt->width, cmpt->height, &num_samples_delta)) {
			jas_eprintf("image too large\n");
			return -1;
		}
		if (!jas_safe_size_add(num_samples, num_samples_delta, &num_samples)) {
			jas_eprintf("image too large\n");
			return -1;
		}
	}

	if (dec->max_samples > 0 && num_samples > dec->max_samples) {
		jas_eprintf("maximum number of samples exceeded (%zu > %zu)\n",
		  num_samples, dec->max_samples);
		return -1;
	}

	dec->image = 0;

	dec->numhtiles = JPC_CEILDIV(dec->xend - dec->tilexoff, dec->tilewidth);
	dec->numvtiles = JPC_CEILDIV(dec->yend - dec->tileyoff, dec->tileheight);
	assert(dec->numhtiles >= 0);
	assert(dec->numvtiles >= 0);
	if (!jas_safe_size_mul(dec->numhtiles, dec->numvtiles, &size) ||
	  size > INT_MAX) {
		return -1;
	}
	if (dec->max_samples > 0 && size > dec->max_samples / 16 / 16) {
		/* avoid Denial of Service by a malicious input file
		   with millions of tiny tiles; if max_samples is
		   configured, then assume the tiles are at least
		   16x16, and don't allow more than this number of
		   tiles */
		return -1;
	}
	if (dec->max_samples > 0 && size > dec->max_samples / dec->numcomps / 16) {
		/* another DoS check: since each tile allocates an
		   array of components, this check attempts to catch
		   excessive tile*component numbers */
		return -1;
	}
	dec->numtiles = size;
	JAS_DBGLOG(10, ("numtiles = %d; numhtiles = %d; numvtiles = %d;\n",
	  dec->numtiles, dec->numhtiles, dec->numvtiles));
	if (!(dec->tiles = jas_alloc2(dec->numtiles, sizeof(jpc_dec_tile_t)))) {
		return -1;
	}

	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
	  ++tile) {
		/* initialize all tiles with JPC_TILE_DONE so
		   jpc_dec_destroy() knows which ones need a
		   jpc_dec_tilefini() call; they are not actually
		   "done", of course */
		tile->state = JPC_TILE_DONE;
	}

	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
	  ++tile) {
		htileno = tileno % dec->numhtiles;
		vtileno = tileno / dec->numhtiles;
		tile->realmode = 0;
		tile->state = JPC_TILE_INIT;
		tile->xstart = JAS_MAX(dec->tilexoff + htileno * dec->tilewidth,
		  dec->xstart);
		tile->ystart = JAS_MAX(dec->tileyoff + vtileno * dec->tileheight,
		  dec->ystart);
		tile->xend = JAS_MIN(dec->tilexoff + (htileno + 1) *
		  dec->tilewidth, dec->xend);
		tile->yend = JAS_MIN(dec->tileyoff + (vtileno + 1) *
		  dec->tileheight, dec->yend);
		tile->numparts = 0;
		tile->partno = 0;
		tile->pkthdrstream = 0;
		tile->pkthdrstreampos = 0;
		tile->pptstab = 0;
		tile->cp = 0;
		tile->pi = 0;
		if (!(tile->tcomps = jas_alloc2(dec->numcomps,
		  sizeof(jpc_dec_tcomp_t)))) {
			return -1;
		}
		for (compno = 0, cmpt = dec->cmpts, tcomp = tile->tcomps;
		  compno < dec->numcomps; ++compno, ++cmpt, ++tcomp) {
			tcomp->rlvls = 0;
			tcomp->numrlvls = 0;
			tcomp->data = 0;
			tcomp->xstart = JPC_CEILDIV(tile->xstart, cmpt->hstep);
			tcomp->ystart = JPC_CEILDIV(tile->ystart, cmpt->vstep);
			tcomp->xend = JPC_CEILDIV(tile->xend, cmpt->hstep);
			tcomp->yend = JPC_CEILDIV(tile->yend, cmpt->vstep);
			tcomp->tsfb = 0;
		}
	}

	dec->pkthdrstreams = 0;

	/* We should expect to encounter other main header marker segments
	  or an SOT marker segment next. */
	dec->state = JPC_MH;

	return 0;
}

#define	JPC_MH		0x0004

typedef struct {

	uint_fast16_t id;
	/* The marker segment type. */

	int validstates;
	/* The states in which this type of marker segment can be
	  validly encountered. */

	int (*action)(jpc_dec_t *dec, jpc_ms_t *ms);
	/* The action to take upon encountering this type of marker segment. */

} jpc_dec_mstabent_t;

static int jpc_dec_dump(jpc_dec_t *dec, FILE *out);

static jpc_ppxstab_t *jpc_ppxstab_create(void);

static void jpc_ppxstab_destroy(jpc_ppxstab_t *tab);

static int jpc_ppxstab_grow(jpc_ppxstab_t *tab, int maxents);

static int jpc_ppxstab_insert(jpc_ppxstab_t *tab, jpc_ppxstabent_t *ent);

static jpc_streamlist_t *jpc_ppmstabtostreams(jpc_ppxstab_t *tab);

static int jpc_pptstabwrite(jas_stream_t *out, jpc_ppxstab_t *tab);

static jpc_ppxstabent_t *jpc_ppxstabent_create(void);

static void jpc_ppxstabent_destroy(jpc_ppxstabent_t *ent);

static int jpc_streamlist_numstreams(jpc_streamlist_t *streamlist);

static jpc_streamlist_t *jpc_streamlist_create(void);

static int jpc_streamlist_insert(jpc_streamlist_t *streamlist, int streamno,
  jas_stream_t *stream);

static jas_stream_t *jpc_streamlist_remove(jpc_streamlist_t *streamlist, int streamno);

static void jpc_streamlist_destroy(jpc_streamlist_t *streamlist);

static void jpc_dec_cp_resetflags(jpc_dec_cp_t *cp);

static jpc_dec_cp_t *jpc_dec_cp_create(uint_fast16_t numcomps);

static int jpc_dec_cp_isvalid(jpc_dec_cp_t *cp);

static jpc_dec_cp_t *jpc_dec_cp_copy(jpc_dec_cp_t *cp);

static int jpc_dec_cp_setfromcod(jpc_dec_cp_t *cp, jpc_cod_t *cod);

static int jpc_dec_cp_setfromcoc(jpc_dec_cp_t *cp, jpc_coc_t *coc);

static int jpc_dec_cp_setfromcox(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
  jpc_coxcp_t *compparms, int flags);

static int jpc_dec_cp_setfromqcd(jpc_dec_cp_t *cp, jpc_qcd_t *qcd);

static int jpc_dec_cp_setfromqcc(jpc_dec_cp_t *cp, jpc_qcc_t *qcc);

static int jpc_dec_cp_setfromqcx(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
  jpc_qcxcp_t *compparms, int flags);

static int jpc_dec_cp_setfromrgn(jpc_dec_cp_t *cp, jpc_rgn_t *rgn);

static int jpc_dec_cp_prepare(jpc_dec_cp_t *cp);

static void jpc_dec_cp_destroy(jpc_dec_cp_t *cp);

static int jpc_dec_cp_setfrompoc(jpc_dec_cp_t *cp, jpc_poc_t *poc, int reset);

static int jpc_pi_addpchgfrompoc(jpc_pi_t *pi, jpc_poc_t *poc);

static int jpc_dec_decode(jpc_dec_t *dec);

static jpc_dec_t *jpc_dec_create(jpc_dec_importopts_t *impopts,
  jas_stream_t *in);


static int jpc_dec_dump(jpc_dec_t *dec, FILE *out)
{
	jpc_dec_tile_t *tile;
	int tileno;
	jpc_dec_tcomp_t *tcomp;
	int compno;
	jpc_dec_rlvl_t *rlvl;
	unsigned rlvlno;
	jpc_dec_band_t *band;
	int bandno;
	jpc_dec_prc_t *prc;
	int prcno;
	jpc_dec_cblk_t *cblk;
	int cblkno;

	assert(!dec->numtiles || dec->tiles);
	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles;
	  ++tileno, ++tile) {
		assert(!dec->numcomps || tile->tcomps);
		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
		  ++compno, ++tcomp) {
			for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno <
			  tcomp->numrlvls; ++rlvlno, ++rlvl) {
				fprintf(out, "RESOLUTION LEVEL %d\n", rlvlno);
				fprintf(out, "xs = %"PRIuFAST32", ys = %"PRIuFAST32", xe = %"PRIuFAST32", ye = %"PRIuFAST32", w = %"PRIuFAST32", h = %"PRIuFAST32"\n",
				  rlvl->xstart, rlvl->ystart, rlvl->xend, rlvl->yend,
				  rlvl->xend - rlvl->xstart, rlvl->yend - rlvl->ystart);
				assert(!rlvl->numbands || rlvl->bands);
				for (bandno = 0, band = rlvl->bands;
				  bandno < rlvl->numbands; ++bandno, ++band) {
					fprintf(out, "BAND %d\n", bandno);
					if (!band->data) {
						fprintf(out, "band has no data (null pointer)\n");
						assert(!band->prcs);
						continue;
					}
					fprintf(out, "xs = %"PRIiFAST32", ys = %"PRIiFAST32", xe = %"PRIiFAST32", ye = %"PRIiFAST32", w = %"PRIiFAST32", h = %"PRIiFAST32"\n",
					  jas_seq2d_xstart(band->data),
					  jas_seq2d_ystart(band->data),
					  jas_seq2d_xend(band->data),
					  jas_seq2d_yend(band->data),
					  jas_seq2d_xend(band->data) -
					  jas_seq2d_xstart(band->data),
					  jas_seq2d_yend(band->data) -
					  jas_seq2d_ystart(band->data));
					assert(!rlvl->numprcs || band->prcs);
					for (prcno = 0, prc = band->prcs;
					  prcno < rlvl->numprcs; ++prcno,
					  ++prc) {
						fprintf(out, "CODE BLOCK GROUP %d\n", prcno);
						fprintf(out, "xs = %"PRIuFAST32", ys = %"PRIuFAST32", xe = %"PRIuFAST32", ye = %"PRIuFAST32", w = %"PRIuFAST32", h = %"PRIuFAST32"\n",
						  prc->xstart, prc->ystart, prc->xend, prc->yend,
						  prc->xend - prc->xstart, prc->yend - prc->ystart);
						assert(!prc->numcblks || prc->cblks);
						for (cblkno = 0, cblk =
						  prc->cblks; cblkno <
						  prc->numcblks; ++cblkno,
						  ++cblk) {
							fprintf(out, "CODE BLOCK %d\n", cblkno);
							fprintf(out, "xs = %"PRIiFAST32", ys = %"PRIiFAST32", xe = %"PRIiFAST32", ye = %"PRIiFAST32", w = %"PRIiFAST32", h = %"PRIiFAST32"\n",
							  jas_seq2d_xstart(cblk->data),
							  jas_seq2d_ystart(cblk->data),
							  jas_seq2d_xend(cblk->data),
							  jas_seq2d_yend(cblk->data),
							  jas_seq2d_xend(cblk->data) -
							  jas_seq2d_xstart(cblk->data),
							  jas_seq2d_yend(cblk->data) -
							  jas_seq2d_ystart(cblk->data));
						}
					}
				}
			}
		}
	}

	return 0;
}


void LibRaw::process_Sony_0x9403(uchar *buf, ushort len)
{
  if (len < 6)
    return;
  uchar bufx = SonySubstitution[buf[4]];
  if ((bufx == 0x00) || (bufx == 0x94))
    return;

  imCommon.SensorTemperature = (float)((short)SonySubstitution[buf[5]]);

  return;
}

additional checks in parseSonySRF

parseSonySR2: buffer size check


void LibRaw::parseSonySRF(unsigned len)
{

  if ((len > 0xfffff) || (len == 0))
    return;

  INT64 save = ftell(ifp);
  INT64 offset =
      0x0310c0 - save; /* for non-DNG this value normally is 0x8ddc */
  if (len < offset || offset < 0)
    return;
  INT64 decrypt_len = offset >> 2; /* master key offset value is the next
                                      un-encrypted metadata field after SRF0 */

  unsigned i, nWB;
  unsigned MasterKey, SRF2Key, RawDataKey;
  INT64 srf_offset, tag_offset, tag_data, tag_dataoffset;
  int tag_dataunitlen;
  uchar *srf_buf;
  ushort entries;
  unsigned tag_id, tag_type, tag_datalen;

  srf_buf = (uchar *)malloc(len+64);
  fread(srf_buf, len, 1, ifp);

  offset += srf_buf[offset] << 2;

#define CHECKBUFFER_SGET4(offset)                                              \
  do                                                                           \
  {                                                                            \
    if ((((offset) + 4) > len) || ((offset) < 0))                              \
      goto restore_after_parseSonySRF;                                         \
  } while (0)

#define CHECKBUFFER_SGET2(offset)                                              \
  do                                                                           \
  {                                                                            \
    if ( ((offset + 2) > len) || ((offset) < 0))                               \
      goto restore_after_parseSonySRF;                                         \
  } while (0)

  CHECKBUFFER_SGET4(offset);

  /* master key is stored in big endian */
  MasterKey = ((unsigned)srf_buf[offset] << 24) |
              ((unsigned)srf_buf[offset + 1] << 16) |
              ((unsigned)srf_buf[offset + 2] << 8) |
              (unsigned)srf_buf[offset + 3];

  /* skip SRF0 */
  srf_offset = 0;
  CHECKBUFFER_SGET2(srf_offset);
  entries = sget2(srf_buf + srf_offset);
  if (entries > 1000)
    goto restore_after_parseSonySRF;
  offset = srf_offset + 2;
  CHECKBUFFER_SGET4(offset);
  CHECKBUFFER_SGET4(offset + 12 * entries);
  srf_offset = sget4(srf_buf + offset + 12 * entries) -
               save; /* SRF0 ends with SRF1 abs. position */

  /* get SRF1, it has fixed 40 bytes length and contains keys to decode metadata
   * and raw data */
  if (srf_offset < 0 || decrypt_len < srf_offset / 4)
    goto restore_after_parseSonySRF;
  sony_decrypt((unsigned *)(srf_buf + srf_offset), decrypt_len - srf_offset / 4,
               1, MasterKey);
  CHECKBUFFER_SGET2(srf_offset);
  entries = sget2(srf_buf + srf_offset);
  if (entries > 1000)
    goto restore_after_parseSonySRF;
  offset = srf_offset + 2;
  tag_offset = offset;

  while (entries--) {
    if (tiff_sget (save, srf_buf, len,
                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
                   &tag_datalen, &tag_dataunitlen) == 0) {
      if (tag_id == 0x0000) {
		  CHECKBUFFER_SGET4(tag_dataoffset);
		  SRF2Key = sget4(srf_buf + tag_dataoffset);
      } else if (tag_id == 0x0001) {
		  CHECKBUFFER_SGET4(tag_dataoffset);
		  RawDataKey = sget4(srf_buf + tag_dataoffset);
      }
    } else goto restore_after_parseSonySRF;
  }
  offset = tag_offset;

  /* get SRF2 */
  CHECKBUFFER_SGET4(offset);
  srf_offset =
      sget4(srf_buf + offset) - save; /* SRFn ends with SRFn+1 position */
  if (srf_offset < 0 || decrypt_len < srf_offset / 4)
    goto restore_after_parseSonySRF;
  sony_decrypt((unsigned *)(srf_buf + srf_offset), decrypt_len - srf_offset / 4,
               1, SRF2Key);
  CHECKBUFFER_SGET2(srf_offset);
  entries = sget2(srf_buf + srf_offset);
  if (entries > 1000)
    goto restore_after_parseSonySRF;
  offset = srf_offset + 2;
  tag_offset = offset;

  while (entries--) {
	  if (tiff_sget(save, srf_buf, len,
                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
                   &tag_datalen, &tag_dataunitlen) == 0) {
      if ((tag_id >= 0x00c0) && (tag_id <= 0x00ce)) {
        i = (tag_id - 0x00c0) % 3;
        nWB = (tag_id - 0x00c0) / 3;
		CHECKBUFFER_SGET4(tag_dataoffset);
		icWBC[Sony_SRF_wb_list[nWB]][i] = sget4(srf_buf + tag_dataoffset);
        if (i == 1) {
          icWBC[Sony_SRF_wb_list[nWB]][3] =
            icWBC[Sony_SRF_wb_list[nWB]][i];
        }
      } else if ((tag_id >= 0x00d0) && (tag_id <= 0x00d2)) {
        i = (tag_id - 0x00d0) % 3;
		CHECKBUFFER_SGET4(tag_dataoffset);
		cam_mul[i] = sget4(srf_buf + tag_dataoffset);
        if (i == 1) {
          cam_mul[3] = cam_mul[i];
        }
      } else switch (tag_id) {
        /*
        0x0002  SRF6Offset
        0x0003  SRFDataOffset (?)
        0x0004  RawDataOffset
        0x0005  RawDataLength
        */
      case 0x0043:
		  CHECKBUFFER_SGET4(tag_dataoffset); // need to add extra space
		  ilm.MaxAp4MaxFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
        break;
      case 0x0044:
		  CHECKBUFFER_SGET4(tag_dataoffset);
		  ilm.MaxAp4MinFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
        break;
      case 0x0045:
		  CHECKBUFFER_SGET4(tag_dataoffset);
		  ilm.MinFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
        break;
      case 0x0046:
		  CHECKBUFFER_SGET4(tag_dataoffset);
		  ilm.MaxFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
        break;
      }
    } else goto restore_after_parseSonySRF;
  }
  offset = tag_offset;

restore_after_parseSonySRF:
  free(srf_buf);
  fseek(ifp, save, SEEK_SET);
#undef CHECKBUFFER_SGET4
#undef CHECKBUFFER_SGET2
}


void LibRaw::process_Sony_0x9402(uchar *buf, ushort len)
{

  if (len < 23)
    return;

  imgdata.shootinginfo.FocusMode = SonySubstitution[buf[0x16]];

  if ((imSony.CameraType == LIBRAW_SONY_SLT) ||
      (imSony.CameraType == LIBRAW_SONY_ILCA))
    return;

  uchar bufx = buf[0x00];
  if ((bufx == 0x05) || (bufx == 0xff) || (buf[0x02] != 0xff))
    return;

  imCommon.AmbientTemperature =
      (float)((short)SonySubstitution[buf[0x04]]);

  return;
}


void LibRaw::parseSonySR2(uchar *cbuf_SR2, unsigned SR2SubIFDOffset,
                          unsigned SR2SubIFDLength, unsigned dng_writer)
{
  unsigned c;
  unsigned entries, tag_id, tag_type, tag_datalen;
  INT64 sr2_offset, tag_offset, tag_data, tag_dataoffset;
  int TagProcessed;
  int tag_dataunitlen;
  float num;
  int i;
  int WBCTC_count;
#define CHECKBUFFER_N(offset,N)                                     \
  do                                                                \
  {                                                                 \
    if ((((offset) + (N)) > SR2SubIFDLength) || ((offset) < 0))     \
      return;														\
  } while (0)

  CHECKBUFFER_N(0, 2);
  entries = sget2(cbuf_SR2);
  if (entries > 1000)
    return;
  tag_offset = 2;
  WBCTC_count = 0;
  while (entries--) {
    if (tiff_sget (SR2SubIFDOffset, cbuf_SR2, SR2SubIFDLength,
                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
                   &tag_datalen, &tag_dataunitlen) == 0) {
      TagProcessed = 0;
      if (dng_writer == nonDNG) {
        switch (tag_id) {
        case 0x7300:
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4,0);
			FORC4 cblack[c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
          TagProcessed = 1;
          break;
        case 0x7303:
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
			FORC4 cam_mul[GRBG_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
          TagProcessed = 1;
          break;
        case 0x7310:
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
			FORC4 cblack[RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
          i = cblack[3];
          FORC3 if (i > cblack[c]) i = cblack[c];
          FORC4 cblack[c] -= i;
          black = i;
          TagProcessed = 1;
          break;
        case 0x7313:
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
			FORC4 cam_mul[RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
          TagProcessed = 1;
          break;
        case 0x74a0:
			CHECKBUFFER_N(tag_dataoffset, 4);
			ilm.MaxAp4MaxFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
          TagProcessed = 1;
          break;
        case 0x74a1:
			CHECKBUFFER_N(tag_dataoffset, 4);
			ilm.MaxAp4MinFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
          TagProcessed = 1;
          break;
        case 0x74a2:
			CHECKBUFFER_N(tag_dataoffset, 4);
			ilm.MaxFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
          TagProcessed = 1;
          break;
        case 0x74a3:
			CHECKBUFFER_N(tag_dataoffset, 4);
			ilm.MinFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
          TagProcessed = 1;
          break;
        case 0x7800:
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 8, 2);
		  for (i = 0; i < 3; i++)
          {
            num = 0.0;
            for (c = 0; c < 3; c++)
            {
              imgdata.color.ccm[i][c] =
                  (float)((short)sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * (i * 3 + c)));
              num += imgdata.color.ccm[i][c];
            }
            if (num > 0.01)
              FORC3 imgdata.color.ccm[i][c] = imgdata.color.ccm[i][c] / num;
          }
          TagProcessed = 1;
          break;
        case 0x787f:
          if (tag_datalen == 3)
          {
			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
			  FORC3 imgdata.color.linear_max[c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
            imgdata.color.linear_max[3] = imgdata.color.linear_max[1];
          }
          else if (tag_datalen == 1)
          {
			  CHECKBUFFER_N(tag_dataoffset, 2);
			  imgdata.color.linear_max[0] = imgdata.color.linear_max[1] =
                imgdata.color.linear_max[2] = imgdata.color.linear_max[3] =
                    sget2(cbuf_SR2 + tag_dataoffset);
          }
          TagProcessed = 1;
          break;
        }
      }

      if (!TagProcessed) {
        if ((tag_id >= 0x7480) && (tag_id <= 0x7486)) {
          i = tag_id - 0x7480;
          if (Sony_SR2_wb_list[i] > 255) {
            icWBCCTC[WBCTC_count][0] = Sony_SR2_wb_list[i];
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
			FORC3 icWBCCTC[WBCTC_count][c + 1] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
            icWBCCTC[WBCTC_count][4] = icWBCCTC[WBCTC_count][2];
            WBCTC_count++;
          } else {
			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
			  FORC3 icWBC[Sony_SR2_wb_list[i]][c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
            icWBC[Sony_SR2_wb_list[i]][3] = icWBC[Sony_SR2_wb_list[i]][1];
          }
        } else if ((tag_id >= 0x7820) && (tag_id <= 0x782d)) {
          i = tag_id - 0x7820;
          if (Sony_SR2_wb_list1[i] > 255) {
            icWBCCTC[WBCTC_count][0] = Sony_SR2_wb_list1[i];
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
			FORC3 icWBCCTC[WBCTC_count][c + 1] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
            icWBCCTC[WBCTC_count][4] = icWBCCTC[WBCTC_count][2];
            if (Sony_SR2_wb_list1[i] == 3200) {
              FORC3 icWBC[LIBRAW_WBI_StudioTungsten][c] = icWBCCTC[WBCTC_count][c + 1];
              icWBC[LIBRAW_WBI_StudioTungsten][3] = icWBC[LIBRAW_WBI_StudioTungsten][1];
            }
            WBCTC_count++;
          } else {
			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
			  FORC3 icWBC[Sony_SR2_wb_list1[i]][c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
            icWBC[Sony_SR2_wb_list1[i]][3] = icWBC[Sony_SR2_wb_list1[i]][1];
          }
        } else if (tag_id == 0x7302) {
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 3, 2);
			FORC4 icWBC[LIBRAW_WBI_Auto][GRBG_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
        } else if (tag_id == 0x7312) {
			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 3, 2);
			FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
        }
      }
    }
  }
}


void LibRaw::setSonyBodyFeatures(unsigned long long id)
{
  ushort idx;
  static const struct
  {
    ushort scf[11];
    /*
    scf[0]  camera id
    scf[1]  camera format
    scf[2]  camera mount: Minolta A, Sony E, fixed,
    scf[3]  camera type: DSLR, NEX, SLT, ILCE, ILCA, DSC
    scf[4]  lens mount
    scf[5]  tag 0x2010 group (0 if not used)
    scf[6]  offset of Sony ISO in 0x2010 table, 0xffff if not valid
    scf[7]  offset of ShutterCount3 in 0x9050 table, 0xffff if not valid
    scf[8]  offset of MeteringMode in 0x2010 table, 0xffff if not valid
    scf[9]  offset of ExposureProgram in 0x2010 table, 0xffff if not valid
    scf[10] offset of ReleaseMode2 in 0x2010 table, 0xffff if not valid
    */
  } SonyCamFeatures[] = {
      {SonyID_DSLR_A100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A900, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A700, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A200, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A350, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A300, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A900, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A380, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A330, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A230, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A290, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x10b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x10c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A850, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A850, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x10f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x110, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A550, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A500, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A450, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x114, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x115, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_NEX_5, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_NEX_3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_SLT_A33, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_SLT_A55, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A560, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSLR_A580, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_NEX_C3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_SLT_A35, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_SLT_A65, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 2,
       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
      {SonyID_SLT_A77, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 2,
       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
      {SonyID_NEX_5N, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 1,
       0x113e, 0x01bd, 0x1174, 0x1175, 0x112c},
      {SonyID_NEX_7, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 2,
       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
      {SonyID_NEX_VG20, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 2,
       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
      {SonyID_SLT_A37, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 3,
       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
      {SonyID_SLT_A57, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 3,
       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
      {SonyID_NEX_F3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 3,
       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
      {SonyID_SLT_A99, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_NEX_6, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_NEX_5R, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_DSC_RX100, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 5, 0x1254, 0xffff, 0x11ac, 0x11ad, 0x1160},
      {SonyID_DSC_RX1, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 5, 0x1258, 0xffff, 0x11ac, 0x11ad, 0x1160},
      {SonyID_NEX_VG900, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_NEX_VG30, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {0x12d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_3000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 5,
       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_SLT_A58, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 5,
       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {0x130, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_NEX_3N, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_ILCE_7, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_NEX_5T, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
      {SonyID_DSC_RX100M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 6, 0x113c, 0xffff, 0x1064, 0x1065, 0x1018},
      {SonyID_DSC_RX10, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 7, 0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX1R, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 5, 0x1258, 0xffff, 0x11ac, 0x11ad, 0x1160},
      {SonyID_ILCE_7R, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_6000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_5000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0x01aa, 0x025c, 0x025d, 0x0210},
      {0x13a, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x13b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x13c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSC_RX100M3, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 7, 0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_7S, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCA_77M2, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0,
       7, 0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
      {0x140, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x141, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x142, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x143, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x144, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x145, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x146, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x147, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x148, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x149, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14a, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14e, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x14f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x150, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x151, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x152, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_5100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_7M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX100M4, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX10M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {0x157, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSC_RX1RM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {0x159, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_QX1, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
       0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_7RM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
       0x0346, 0x01cb, 0x025c, 0x025d, 0x0210},
      {0x15c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x15d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_7SM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
       0x0346, 0x01cb, 0x025c, 0x025d, 0x0210},
      {0x15f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {0x160, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCA_68, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0,
       7, 0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCA_99M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0, 8,
       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX10M3, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX100M5, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_6300, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
      {SonyID_ILCE_9, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {0x167, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_6500, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
      {0x169, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_7RM3, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {SonyID_ILCE_7M3, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {SonyID_DSC_RX0, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
      {SonyID_DSC_RX10M4, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
      {SonyID_DSC_RX100M6, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
      {SonyID_DSC_HX99, LIBRAW_FORMAT_1div2p3INCH, LIBRAW_MOUNT_FixedLens,
       LIBRAW_SONY_DSC, LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b,
       0x024c, 0x0208},
      {0x170, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSC_RX100M5A, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
      {0x172, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_6400, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {SonyID_DSC_RX0M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
      {0x175, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_DSC_RX100M7, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
      {SonyID_ILCE_7RM4, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {SonyID_ILCE_9M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {0x179, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
      {SonyID_ILCE_6600, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
      {SonyID_ILCE_6100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},

  };
  ilm.CamID = id;

  if (id == SonyID_DSC_R1)
  {
    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
    imSony.CameraType = LIBRAW_SONY_DSC;
    imSony.group2010 = 0;
    imSony.real_iso_offset = 0xffff;
    imSony.ImageCount3_offset = 0xffff;
    return;
  }
  else
    idx = id - 0x100ULL;

  if ((idx >= 0) && (idx < sizeof SonyCamFeatures / sizeof *SonyCamFeatures))
  {
    if (!SonyCamFeatures[idx].scf[2])
      return;
    ilm.CameraFormat = SonyCamFeatures[idx].scf[1];
    ilm.CameraMount = SonyCamFeatures[idx].scf[2];
    imSony.CameraType = SonyCamFeatures[idx].scf[3];
    if (SonyCamFeatures[idx].scf[4])
      ilm.LensMount = SonyCamFeatures[idx].scf[4];
    imSony.group2010 = SonyCamFeatures[idx].scf[5];
    imSony.real_iso_offset = SonyCamFeatures[idx].scf[6];
    imSony.ImageCount3_offset = SonyCamFeatures[idx].scf[7];
    imSony.MeteringMode_offset = SonyCamFeatures[idx].scf[8];
    imSony.ExposureProgram_offset = SonyCamFeatures[idx].scf[9];
    imSony.ReleaseMode2_offset = SonyCamFeatures[idx].scf[10];
  }

  char *sbstr = strstr(software, " v");
  if (sbstr != NULL)
  {
    sbstr += 2;
    imSony.firmware = atof(sbstr);

    if ((id == SonyID_ILCE_7) ||
        (id == SonyID_ILCE_7R))
    {
      if (imSony.firmware < 1.2f)
        imSony.ImageCount3_offset = 0x01aa;
      else
        imSony.ImageCount3_offset = 0x01c0;
    }
    else if (id == SonyID_ILCE_6000)
    {
      if (imSony.firmware < 2.0f)
        imSony.ImageCount3_offset = 0x01aa;
      else
        imSony.ImageCount3_offset = 0x01c0;
    }
    else if ((id == SonyID_ILCE_7S) ||
             (id == SonyID_ILCE_7M2))
    {
      if (imSony.firmware < 1.2f)
        imSony.ImageCount3_offset = 0x01a0;
      else
        imSony.ImageCount3_offset = 0x01b6;
    }
  }
}


void LibRaw::parseSonyLensType2(uchar a, uchar b)
{
  ushort lid2;
  lid2 = (((ushort)a) << 8) | ((ushort)b);
//  printf ("==>> 2: lid2 %d\n", lid2);
  if (!lid2)
    return;
  if (lid2 < 0x100)
  {
    if ((ilm.AdapterID != 0x4900) && (ilm.AdapterID != 0xef00))
    {
      ilm.AdapterID = lid2;
      switch (lid2)
      {
      case 1:
      case 2:
      case 3:
      case 6:
        ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
        break;
      case 44:
      case 78:
      case 184:
      case 234:
      case 239:
        ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
        break;
      }
    }
  }
  else
    ilm.LensID = lid2;

  if ((lid2 >= 50481) &&
      (lid2 < 50500)) {
    strcpy(ilm.Adapter, "MC-11");
    ilm.AdapterID = 0x4900;
  } else if ((lid2 > 0xef00) &&
             (lid2 < 0xffff) &&
             (lid2 != 0xff00)) {
    ilm.AdapterID = 0xef00;
    ilm.LensID -= ilm.AdapterID;
    ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
  }

  return;
}