ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose,
                                const struct ofputil_async_cfg *basis,
                                struct ofputil_async_cfg *ac)
{
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
    enum ofpraw raw = ofpraw_pull_assert(&b);

    if (raw == OFPRAW_OFPT13_SET_ASYNC ||
        raw == OFPRAW_NXT_SET_ASYNC_CONFIG ||
        raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) {
        const struct nx_async_config *msg = ofpmsg_body(oh);

        *ac = OFPUTIL_ASYNC_CFG_INIT;
        decode_legacy_async_masks(msg->packet_in_mask, OAM_PACKET_IN,
                                  oh->version, ac);
        decode_legacy_async_masks(msg->port_status_mask, OAM_PORT_STATUS,
                                  oh->version, ac);
        decode_legacy_async_masks(msg->flow_removed_mask, OAM_FLOW_REMOVED,
                                  oh->version, ac);
    } else if (raw == OFPRAW_OFPT14_SET_ASYNC ||
               raw == OFPRAW_OFPT14_GET_ASYNC_REPLY ||
               raw == OFPRAW_NXT_SET_ASYNC_CONFIG2) {
        *ac = *basis;
        while (b.size > 0) {
            struct ofpbuf property;
            enum ofperr error;
            uint64_t type;

            error = ofpprop_pull__(&b, &property, 8, 0xfffe, &type);
            if (error) {
                return error;
            }

            const struct ofp14_async_prop *ap
                = get_ofp14_async_config_prop_by_prop_type(type);
            error = (ap
                     ? parse_async_tlv(&property, ap, ac, oh->version, loose)
                     : OFPPROP_UNKNOWN(loose, "async config", type));
            if (error) {
                /* Most messages use OFPBPC_BAD_TYPE but async has its own (who
                 * knows why, it's OpenFlow. */
                if (error == OFPERR_OFPBPC_BAD_TYPE) {
                    error = OFPERR_OFPACFC_UNSUPPORTED;
                }
                return error;
            }
        }
    } else {
        return OFPERR_OFPBRC_BAD_VERSION;
    }
    return 0;
}


ofputil_put_ofp11_bucket(const struct ofputil_bucket *bucket,
                         struct ofpbuf *openflow, enum ofp_version ofp_version)
{
    struct ofp11_bucket *ob;
    size_t start;

    start = openflow->size;
    ofpbuf_put_zeros(openflow, sizeof *ob);
    ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len,
                                openflow, ofp_version);
    ob = ofpbuf_at_assert(openflow, start, sizeof *ob);
    ob->len = htons(openflow->size - start);
    ob->weight = htons(bucket->weight);
    ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
    ob->watch_group = htonl(bucket->watch_group);
}


ofputil_version_from_string(const char *s)
{
    if (!strcasecmp(s, "OpenFlow10")) {
        return OFP10_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow11")) {
        return OFP11_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow12")) {
        return OFP12_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow13")) {
        return OFP13_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow14")) {
        return OFP14_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow15")) {
        return OFP15_VERSION;
    }
    if (!strcasecmp(s, "OpenFlow16")) {
        return OFP16_VERSION;
    }
    return 0;
}


ofputil_decode_role_status(const struct ofp_header *oh,
                           struct ofputil_role_status *rs)
{
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
    enum ofpraw raw = ofpraw_pull_assert(&b);
    ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS);

    const struct ofp14_role_status *r = b.msg;
    if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
        r->role != htonl(OFPCR12_ROLE_EQUAL) &&
        r->role != htonl(OFPCR12_ROLE_MASTER) &&
        r->role != htonl(OFPCR12_ROLE_SLAVE)) {
        return OFPERR_OFPRRFC_BAD_ROLE;
    }

    rs->role = ntohl(r->role);
    rs->generation_id = ntohll(r->generation_id);
    rs->reason = r->reason;

    return 0;
}


ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
                                struct ofpbuf *msg, enum ofp_version version)
{
    ofputil_init_group_properties(&gd->props);

    switch (version)
    {
    case OFP11_VERSION:
    case OFP12_VERSION:
    case OFP13_VERSION:
    case OFP14_VERSION:
        return ofputil_decode_ofp11_group_desc_reply(gd, msg, version);

    case OFP15_VERSION:
    case OFP16_VERSION:
        return ofputil_decode_ofp15_group_desc_reply(gd, msg, version);

    case OFP10_VERSION:
    default:
        OVS_NOT_REACHED();
    }
}


ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
                                      const struct ovs_list *buckets,
                                      struct ovs_list *replies,
                                      enum ofp_version version)
{
    struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
    struct ofp15_group_desc_stats *ogds;
    struct ofputil_bucket *bucket;
    size_t start_ogds, start_buckets;

    start_ogds = reply->size;
    ofpbuf_put_zeros(reply, sizeof *ogds);
    start_buckets = reply->size;
    LIST_FOR_EACH (bucket, list_node, buckets) {
        ofputil_put_ofp15_bucket(bucket, bucket->bucket_id,
                                 gds->type, reply, version);
    }
    ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
    ogds->type = gds->type;
    ogds->group_id = htonl(gds->group_id);
    ogds->bucket_list_len =  htons(reply->size - start_buckets);

    /* Add group properties */
    if (gds->props.selection_method[0]) {
        ofputil_put_group_prop_ntr_selection_method(version, &gds->props,
                                                    reply);
    }
    ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
    ogds->length = htons(reply->size - start_ogds);

    ofpmp_postappend(replies, start_ogds);
}


ofputil_pull_ipfix_stats(struct ofputil_ipfix_stats *is, struct ofpbuf *msg)
{
    enum ofperr error;
    enum ofpraw raw;

    memset(is, 0xFF, sizeof (*is));

    error = (msg->header ? ofpraw_decode(&raw, msg->header)
             : ofpraw_pull(&raw, msg));
    if (error) {
        return error;
    }

    if (!msg->size) {
        return EOF;
    } else if (raw == OFPRAW_NXST_IPFIX_BRIDGE_REPLY ||
               raw == OFPRAW_NXST_IPFIX_FLOW_REPLY) {
        struct nx_ipfix_stats_reply *reply;

        reply = ofpbuf_try_pull(msg, sizeof *reply);
        return ofputil_ipfix_stats_from_nx(is, reply);
    } else {
        OVS_NOT_REACHED();
    }
}


ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version,
                                   struct ofputil_bundle_ctrl_msg *bc)
{
    struct ofpbuf *request;
    struct ofp14_bundle_ctrl_msg *m;

    switch (ofp_version) {
    case OFP10_VERSION:
    case OFP11_VERSION:
    case OFP12_VERSION:
        ovs_fatal(0, "bundles need OpenFlow 1.3 or later "
                     "(\'-O OpenFlow14\')");
    case OFP13_VERSION:
    case OFP14_VERSION:
    case OFP15_VERSION:
    case OFP16_VERSION:
        request = ofpraw_alloc(ofp_version == OFP13_VERSION
                               ? OFPRAW_ONFT13_BUNDLE_CONTROL
                               : OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0);
        m = ofpbuf_put_zeros(request, sizeof *m);

        m->bundle_id = htonl(bc->bundle_id);
        m->type = htons(bc->type);
        m->flags = htons(bc->flags);
        break;
    default:
        OVS_NOT_REACHED();
    }

    return request;
}


ofputil_encode_get_config_reply(const struct ofp_header *request,
                                const struct ofputil_switch_config *config)
{
    struct ofpbuf *b = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY,
                                          request, 0);
    return ofputil_put_switch_config(config, b);
}


ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
                         const struct tun_table *tun_table,
                         const struct vl_mff_map *vl_mff_map,
                         struct ofputil_packet_in *pin,
                         size_t *total_lenp, uint32_t *buffer_idp,
                         struct ofpbuf *continuation)
{
    uint32_t buffer_id;
    size_t total_len;

    memset(pin, 0, sizeof *pin);
    pin->cookie = OVS_BE64_MAX;
    if (continuation) {
        ofpbuf_use_const(continuation, NULL, 0);
    }

    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
    enum ofpraw raw = ofpraw_pull_assert(&b);
    if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
        const struct ofp12_packet_in *opi = ofpbuf_pull(&b, sizeof *opi);
        const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
                                  ? ofpbuf_pull(&b, sizeof *cookie)
                                  : NULL);
        enum ofperr error = oxm_pull_match_loose(&b, false, tun_table,
                                                 &pin->flow_metadata);
        if (error) {
            return error;
        }

        if (!ofpbuf_try_pull(&b, 2)) {
            return OFPERR_OFPBRC_BAD_LEN;
        }

        pin->reason = opi->reason;
        pin->table_id = opi->table_id;
        buffer_id = ntohl(opi->buffer_id);
        total_len = ntohs(opi->total_len);
        if (cookie) {
            pin->cookie = *cookie;
        }

        pin->packet = b.data;
        pin->packet_len = b.size;
    } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
        const struct ofp10_packet_in *opi;

        opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data));

        pin->packet = CONST_CAST(uint8_t *, opi->data);
        pin->packet_len = b.size;

        match_init_catchall(&pin->flow_metadata);
        match_set_in_port(&pin->flow_metadata,
                          u16_to_ofp(ntohs(opi->in_port)));
        pin->reason = opi->reason;
        buffer_id = ntohl(opi->buffer_id);
        total_len = ntohs(opi->total_len);
    } else if (raw == OFPRAW_OFPT11_PACKET_IN) {
        const struct ofp11_packet_in *opi;
        ofp_port_t in_port;
        enum ofperr error;

        opi = ofpbuf_pull(&b, sizeof *opi);

        pin->packet = b.data;
        pin->packet_len = b.size;

        buffer_id = ntohl(opi->buffer_id);
        error = ofputil_port_from_ofp11(opi->in_port, &in_port);
        if (error) {
            return error;
        }
        match_init_catchall(&pin->flow_metadata);
        match_set_in_port(&pin->flow_metadata, in_port);
        total_len = ntohs(opi->total_len);
        pin->reason = opi->reason;
        pin->table_id = opi->table_id;
    } else if (raw == OFPRAW_NXT_PACKET_IN) {
        const struct nx_packet_in *npi;
        int error;

        npi = ofpbuf_pull(&b, sizeof *npi);
        error = nx_pull_match_loose(&b, ntohs(npi->match_len),
                                    &pin->flow_metadata, NULL, NULL, false,
                                    NULL);
        if (error) {
            return error;
        }

        if (!ofpbuf_try_pull(&b, 2)) {
            return OFPERR_OFPBRC_BAD_LEN;
        }

        pin->reason = npi->reason;
        pin->table_id = npi->table_id;
        pin->cookie = npi->cookie;

        buffer_id = ntohl(npi->buffer_id);
        total_len = ntohs(npi->total_len);

        pin->packet = b.data;
        pin->packet_len = b.size;
    } else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) {
        enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table,
                                                 vl_mff_map, pin, &total_len,
                                                 &buffer_id, continuation);
        if (error) {
            return error;
        }
    } else {
        OVS_NOT_REACHED();
    }

    if (total_lenp) {
        *total_lenp = total_len;
    }
    if (buffer_idp) {
        *buffer_idp = buffer_id;
    }

    return 0;
}


ofputil_encode_group_mod(enum ofp_version ofp_version,
                         const struct ofputil_group_mod *gm)
{

    switch (ofp_version) {
    case OFP10_VERSION:
        bad_group_cmd(gm->command);
        /* fall through */

    case OFP11_VERSION:
    case OFP12_VERSION:
    case OFP13_VERSION:
    case OFP14_VERSION:
        if (gm->command > OFPGC11_DELETE && gm->command != OFPGC11_ADD_OR_MOD) {
            bad_group_cmd(gm->command);
        }
        return ofputil_encode_ofp11_group_mod(ofp_version, gm);

    case OFP15_VERSION:
    case OFP16_VERSION:
        return ofputil_encode_ofp15_group_mod(ofp_version, gm);

    default:
        OVS_NOT_REACHED();
    }
}


ofputil_port_map_put(struct ofputil_port_map *map,
                     ofp_port_t ofp_port, const char *name)
{
    struct ofputil_port_map_node *node;

    /* Look for duplicate name. */
    node = ofputil_port_map_find_by_name(map, name);
    if (node) {
        if (node->ofp_port != ofp_port) {
            node->duplicate = true;
        }
        return;
    }

    /* Look for duplicate number. */
    node = ofputil_port_map_find_by_number(map, ofp_port);
    if (node) {
        node->duplicate = true;
        return;
    }

    /* Add new node. */
    node = xmalloc(sizeof *node);
    hmap_insert(&map->by_number, &node->number_node, hash_ofp_port(ofp_port));
    hmap_insert(&map->by_name, &node->name_node, hash_string(name, 0));
    node->ofp_port = ofp_port;
    node->name = xstrdup(name);
    node->duplicate = false;
}


ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
                        enum ofputil_protocol protocol)
{
    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
    struct ofpbuf *b;

    switch (ofp_version) {
    case OFP10_VERSION: {
        struct ofp10_port_mod *opm;

        b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
        opm = ofpbuf_put_zeros(b, sizeof *opm);
        opm->port_no = htons(ofp_to_u16(pm->port_no));
        opm->hw_addr = pm->hw_addr;
        opm->config = htonl(pm->config & OFPPC10_ALL);
        opm->mask = htonl(pm->mask & OFPPC10_ALL);
        opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
        break;
    }

    case OFP11_VERSION:
    case OFP12_VERSION:
    case OFP13_VERSION: {
        struct ofp11_port_mod *opm;

        b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
        opm = ofpbuf_put_zeros(b, sizeof *opm);
        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
        opm->hw_addr = pm->hw_addr;
        opm->config = htonl(pm->config & OFPPC11_ALL);
        opm->mask = htonl(pm->mask & OFPPC11_ALL);
        opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
        break;
    }
    case OFP14_VERSION:
    case OFP15_VERSION: {
        struct ofp14_port_mod *opm;

        b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, 0);
        opm = ofpbuf_put_zeros(b, sizeof *opm);
        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
        opm->hw_addr = pm->hw_addr;
        opm->config = htonl(pm->config & OFPPC11_ALL);
        opm->mask = htonl(pm->mask & OFPPC11_ALL);

        if (pm->advertise) {
            ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
                             netdev_port_features_to_ofp11(pm->advertise));
        }
        break;
    }
    case OFP16_VERSION: {
        struct ofp16_port_mod *opm;

        b = ofpraw_alloc(OFPRAW_OFPT16_PORT_MOD, ofp_version, 0);
        opm = ofpbuf_put_zeros(b, sizeof *opm);
        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
        if (!eth_addr_is_zero(pm->hw_addr)) {
            opm->hw_addr_type |= htons(OFPPHAT16_EUI48);
            opm->hw_addr = pm->hw_addr;
        }
        if (!eth_addr64_is_zero(pm->hw_addr64)) {
            opm->hw_addr_type |= htons(OFPPHAT16_EUI64);
            opm->hw_addr64 = pm->hw_addr64;
        }
        opm->config = htonl(pm->config & OFPPC11_ALL);
        opm->mask = htonl(pm->mask & OFPPC11_ALL);

        if (pm->advertise) {
            ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
                             netdev_port_features_to_ofp11(pm->advertise));
        }
        break;
    }
    default:
        OVS_NOT_REACHED();
    }

    return b;
}


ofputil_put_async_config__(const struct ofputil_async_cfg *ac,
                           struct ofpbuf *buf, bool tlv,
                           enum ofp_version version, uint32_t oams)
{
    if (!tlv) {
        struct nx_async_config *msg = ofpbuf_put_zeros(buf, sizeof *msg);
        encode_legacy_async_masks(ac, OAM_PACKET_IN, version,
                                  msg->packet_in_mask);
        encode_legacy_async_masks(ac, OAM_PORT_STATUS, version,
                                  msg->port_status_mask);
        encode_legacy_async_masks(ac, OAM_FLOW_REMOVED, version,
                                  msg->flow_removed_mask);
    } else {
        FOR_EACH_ASYNC_PROP (ap) {
            if (oams & (1u << ap->oam)) {
                size_t ofs = buf->size;
                ofpprop_put_be32(buf, ap->prop_type,
                                 encode_async_mask(ac, ap, version));

                /* For experimenter properties, we need to use type 0xfffe for
                 * master and 0xffff for slaves. */
                if (ofpprop_is_experimenter(ap->prop_type)) {
                    struct ofp_prop_experimenter *ope
                        = ofpbuf_at_assert(buf, ofs, sizeof *ope);
                    ope->type = ap->master ? htons(0xffff) : htons(0xfffe);
                }
            }
        }
    }
}


ofputil_append_table_stats_reply(struct ofpbuf *reply,
                                 const struct ofputil_table_stats *stats,
                                 const struct ofputil_table_features *features)
{
    struct ofp_header *oh = reply->header;

    ovs_assert(stats->table_id == features->table_id);

    switch ((enum ofp_version) oh->version) {
    case OFP10_VERSION:
        ofputil_put_ofp10_table_stats(stats, features, reply);
        break;

    case OFP11_VERSION:
        ofputil_put_ofp11_table_stats(stats, features, reply);
        break;

    case OFP12_VERSION:
        ofputil_put_ofp12_table_stats(stats, features, reply);
        break;

    case OFP13_VERSION:
    case OFP14_VERSION:
    case OFP15_VERSION:
    case OFP16_VERSION:
        ofputil_put_ofp13_table_stats(stats, reply);
        break;

    default:
        OVS_NOT_REACHED();
    }
}


parse_table_desc_vacancy_property(struct ofpbuf *property,
                                  struct ofputil_table_desc *td)
{
    struct ofp14_table_mod_prop_vacancy *otv = property->data;

    if (property->size != sizeof *otv) {
        return OFPERR_OFPBPC_BAD_LEN;
    }

    td->table_vacancy.vacancy_down = otv->vacancy_down;
    td->table_vacancy.vacancy_up = otv->vacancy_up;
    td->table_vacancy.vacancy = otv->vacancy;
    return 0;
}


ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops,
                            struct ofp13_port_stats *ps13)
{
    ofputil_port_stats_to_ofp11(ops, &ps13->ps);
    ps13->duration_sec = htonl(ops->duration_sec);
    ps13->duration_nsec = htonl(ops->duration_nsec);
}


ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version)
{
    return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT
            : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON
            : OFPUTIL_TABLE_EVICTION_OFF);
}


ofputil_append_meter_stats(struct ovs_list *replies,
                           const struct ofputil_meter_stats *ms)
{
    struct ofp13_meter_stats *reply;
    uint16_t n = 0;
    uint16_t len;

    len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
    reply = ofpmp_append(replies, len);

    reply->meter_id = htonl(ms->meter_id);
    reply->len = htons(len);
    memset(reply->pad, 0, sizeof reply->pad);
    reply->flow_count = htonl(ms->flow_count);
    reply->packet_in_count = htonll(ms->packet_in_count);
    reply->byte_in_count = htonll(ms->byte_in_count);
    reply->duration_sec = htonl(ms->duration_sec);
    reply->duration_nsec = htonl(ms->duration_nsec);

    for (n = 0; n < ms->n_bands; ++n) {
        const struct ofputil_meter_band_stats *src = &ms->bands[n];
        struct ofp13_meter_band_stats *dst = &reply->band_stats[n];

        dst->packet_band_count = htonll(src->packet_count);
        dst->byte_band_count = htonll(src->byte_count);
    }
}


put_table_action_features(struct ofpbuf *reply,
                          const struct ofputil_table_action_features *taf,
                          enum ofp13_table_feature_prop_type actions_type,
                          enum ofp13_table_feature_prop_type set_fields_type,
                          int miss_offset, enum ofp_version version)
{
    ofpprop_put_bitmap(reply, actions_type + miss_offset,
                       ntohl(ofpact_bitmap_to_openflow(taf->ofpacts,
                                                       version)));
    put_fields_property(reply, &taf->set_fields, NULL,
                        set_fields_type + miss_offset, version);
}


ofputil_encode_meter_request(enum ofp_version ofp_version,
                             enum ofputil_meter_request_type type,
                             uint32_t meter_id)
{
    struct ofpbuf *msg;

    enum ofpraw raw;

    switch (type) {
    case OFPUTIL_METER_CONFIG:
        raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
        break;
    case OFPUTIL_METER_STATS:
        raw = OFPRAW_OFPST13_METER_REQUEST;
        break;
    default:
    case OFPUTIL_METER_FEATURES:
        raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
        break;
    }

    msg = ofpraw_alloc(raw, ofp_version, 0);

    if (type != OFPUTIL_METER_FEATURES) {
        struct ofp13_meter_multipart_request *omr;
        omr = ofpbuf_put_zeros(msg, sizeof *omr);
        omr->meter_id = htonl(meter_id);
    }
    return msg;
}


void key_user_put(struct key_user *user)
{
	if (refcount_dec_and_lock(&user->usage, &key_user_lock)) {
		rb_erase(&user->node, &key_user_tree);
		spin_unlock(&key_user_lock);

		kfree(user);
	}
}


void flush_all_to_thread(struct task_struct *tsk)
{
	if (tsk->thread.regs) {
		preempt_disable();
		BUG_ON(tsk != current);
		save_all(tsk);

#ifdef CONFIG_SPE
		if (tsk->thread.regs->msr & MSR_SPE)
			tsk->thread.spefscr = mfspr(SPRN_SPEFSCR);
#endif

		preempt_enable();
	}
}


void WatchdogException(struct pt_regs *regs)
{
	printk (KERN_EMERG "PowerPC Book-E Watchdog Exception\n");
	WatchdogHandler(regs);
}


void SoftwareEmulation(struct pt_regs *regs)
{
	CHECK_FULL_REGS(regs);

	if (!user_mode(regs)) {
		debugger(regs);
		die("Kernel Mode Unimplemented Instruction or SW FPU Emulation",
			regs, SIGFPE);
	}

	if (!emulate_math(regs))
		return;

	_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
}


static inline void tm_reclaim_task(struct task_struct *tsk)
{
	/* We have to work out if we're switching from/to a task that's in the
	 * middle of a transaction.
	 *
	 * In switching we need to maintain a 2nd register state as
	 * oldtask->thread.ckpt_regs.  We tm_reclaim(oldproc); this saves the
	 * checkpointed (tbegin) state in ckpt_regs, ckfp_state and
	 * ckvr_state
	 *
	 * We also context switch (save) TFHAR/TEXASR/TFIAR in here.
	 */
	struct thread_struct *thr = &tsk->thread;

	if (!thr->regs)
		return;

	if (!MSR_TM_ACTIVE(thr->regs->msr))
		goto out_and_saveregs;

	TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, "
		 "ccr=%lx, msr=%lx, trap=%lx)\n",
		 tsk->pid, thr->regs->nip,
		 thr->regs->ccr, thr->regs->msr,
		 thr->regs->trap);

	tm_reclaim_thread(thr, task_thread_info(tsk), TM_CAUSE_RESCHED);

	TM_DEBUG("--- tm_reclaim on pid %d complete\n",
		 tsk->pid);

out_and_saveregs:
	/* Always save the regs here, even if a transaction's not active.
	 * This context-switches a thread's TM info SPRs.  We do it here to
	 * be consistent with the restore path (in recheckpoint) which
	 * cannot happen later in _switch().
	 */
	tm_save_sprs(thr);
}


long machine_check_early(struct pt_regs *regs)
{
	long handled = 0;

	__this_cpu_inc(irq_stat.mce_exceptions);

	add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);

	if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
		handled = cur_cpu_spec->machine_check_early(regs);
	return handled;
}


void unknown_exception(struct pt_regs *regs)
{
	enum ctx_state prev_state = exception_enter();

	printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
	       regs->nip, regs->msr, regs->trap);

	_exception(SIGTRAP, regs, 0, 0);

	exception_exit(prev_state);
}


static void __giveup_vsx(struct task_struct *tsk)
{
	if (tsk->thread.regs->msr & MSR_FP)
		__giveup_fpu(tsk);
	if (tsk->thread.regs->msr & MSR_VEC)
		__giveup_altivec(tsk);
	tsk->thread.regs->msr &= ~MSR_VSX;
}


void altivec_unavailable_tm(struct pt_regs *regs)
{
	/* See the comments in fp_unavailable_tm().  This function operates
	 * the same way.
	 */

	TM_DEBUG("Vector Unavailable trap whilst transactional at 0x%lx,"
		 "MSR=%lx\n",
		 regs->nip, regs->msr);
	tm_reclaim_current(TM_CAUSE_FAC_UNAV);
	regs->msr |= MSR_VEC;
	tm_recheckpoint(&current->thread, MSR_VEC);
	current->thread.used_vr = 1;

	if (regs->msr & MSR_FP) {
		msr_check_and_set(MSR_FP);
		load_fp_state(&current->thread.fp_state);
		regs->msr |= MSR_VSX;
	}
}


replace_write_s2d	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
{	BUF_UNION	ubuf ;
	int			bufferlen, writecount ;
	sf_count_t	total = 0 ;
	double		scale ;

	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / 0x8000 ;
	bufferlen = ARRAY_LEN (ubuf.dbuf) ;

	while (len > 0)
	{	if (len < bufferlen)
			bufferlen = (int) len ;
		s2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;

		if (psf->peak_info)
			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;

		bd2d_write (ubuf.dbuf, bufferlen) ;

		if (psf->data_endswap == SF_TRUE)
			endswap_double_array (ubuf.dbuf, bufferlen) ;

		writecount = psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
		total += writecount ;
		if (writecount < bufferlen)
			break ;
		len -= writecount ;
		} ;

	return total ;
} /* replace_write_s2d */


d2s_array (const double *src, int count, short *dest, double scale)
{	while (--count >= 0)
	{	dest [count] = lrint (scale * src [count]) ;
		} ;
} /* d2s_array */


host_write_i2d	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
{	BUF_UNION	ubuf ;
	int			bufferlen, writecount ;
	sf_count_t	total = 0 ;
	double		scale ;

	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / (8.0 * 0x10000000) ;
	bufferlen = ARRAY_LEN (ubuf.dbuf) ;

	while (len > 0)
	{	if (len < bufferlen)
			bufferlen = (int) len ;
		i2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;

		if (psf->peak_info)
			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;

		if (psf->data_endswap == SF_TRUE)
			endswap_double_array (ubuf.dbuf, bufferlen) ;

		writecount = psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
		total += writecount ;
		if (writecount < bufferlen)
			break ;
		len -= writecount ;
		} ;

	return total ;
} /* host_write_i2d */


f2d_array (const float *src, double *dest, int count)
{	while (--count >= 0)
	{	dest [count] = src [count] ;
		} ;
} /* f2d_array */


host_write_f2d	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
{	BUF_UNION	ubuf ;
	int			bufferlen, writecount ;
	sf_count_t	total = 0 ;

	bufferlen = ARRAY_LEN (ubuf.dbuf) ;

	while (len > 0)
	{	if (len < bufferlen)
			bufferlen = (int) len ;
		f2d_array (ptr + total, ubuf.dbuf, bufferlen) ;

		if (psf->peak_info)
			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;

		if (psf->data_endswap == SF_TRUE)
			endswap_double_array (ubuf.dbuf, bufferlen) ;

		writecount = psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
		total += writecount ;
		if (writecount < bufferlen)
			break ;
		len -= writecount ;
		} ;

	return total ;
} /* host_write_f2d */


sg_add_sfp(Sg_device * sdp)
{
	Sg_fd *sfp;
	unsigned long iflags;
	int bufflen;

	sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);
	if (!sfp)
		return ERR_PTR(-ENOMEM);

	init_waitqueue_head(&sfp->read_wait);
	rwlock_init(&sfp->rq_list_lock);
	INIT_LIST_HEAD(&sfp->rq_list);
	kref_init(&sfp->f_ref);
	mutex_init(&sfp->f_mutex);
	sfp->timeout = SG_DEFAULT_TIMEOUT;
	sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
	sfp->force_packid = SG_DEF_FORCE_PACK_ID;
	sfp->cmd_q = SG_DEF_COMMAND_Q;
	sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
	sfp->parentdp = sdp;
	write_lock_irqsave(&sdp->sfd_lock, iflags);
	if (atomic_read(&sdp->detaching)) {
		write_unlock_irqrestore(&sdp->sfd_lock, iflags);
		return ERR_PTR(-ENODEV);
	}
	list_add_tail(&sfp->sfd_siblings, &sdp->sfds);
	write_unlock_irqrestore(&sdp->sfd_lock, iflags);
	SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
				      "sg_add_sfp: sfp=0x%p\n", sfp));
	if (unlikely(sg_big_buff != def_reserved_size))
		sg_big_buff = def_reserved_size;

	bufflen = min_t(int, sg_big_buff,
			max_sectors_bytes(sdp->device->request_queue));
	sg_build_reserve(sfp, bufflen);
	SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
				      "sg_add_sfp: bufflen=%d, k_use_sg=%d\n",
				      sfp->reserve.bufflen,
				      sfp->reserve.k_use_sg));

	kref_get(&sdp->d_ref);
	__module_get(THIS_MODULE);
	return sfp;
}


static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  Image
    *chroma_image,
    *image,
    *resize_image;

  InterlaceType
    interlace;

  MagickBooleanType
    status;

  register const Quantum
    *chroma_pixels;

  register ssize_t
    x;

  register Quantum
    *q;

  register unsigned char
    *p;

  ssize_t
    count,
    horizontal_factor,
    vertical_factor,
    y;

  size_t
    length,
    quantum;

  unsigned char
    *scanline;

  /*
    Allocate image structure.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  if ((image->columns == 0) || (image->rows == 0))
    ThrowReaderException(OptionError,"MustSpecifyImageSize");
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  quantum=(ssize_t) (image->depth <= 8 ? 1 : 2);
  interlace=image_info->interlace;
  horizontal_factor=2;
  vertical_factor=2;
  if (image_info->sampling_factor != (char *) NULL)
    {
      GeometryInfo
        geometry_info;

      MagickStatusType
        flags;

      flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
      horizontal_factor=(ssize_t) geometry_info.rho;
      vertical_factor=(ssize_t) geometry_info.sigma;
      if ((flags & SigmaValue) == 0)
        vertical_factor=horizontal_factor;
      if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
          (vertical_factor != 1) && (vertical_factor != 2))
        ThrowReaderException(CorruptImageError,"UnexpectedSamplingFactor");
    }
  if ((interlace == UndefinedInterlace) ||
      ((interlace == NoInterlace) && (vertical_factor == 2)))
    {
      interlace=NoInterlace;    /* CCIR 4:2:2 */
      if (vertical_factor == 2)
        interlace=PlaneInterlace; /* CCIR 4:1:1 */
    }
  if (interlace != PartitionInterlace)
    {
      /*
        Open image file.
      */
      status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
      if (status == MagickFalse)
        {
          image=DestroyImageList(image);
          return((Image *) NULL);
        }
      if (DiscardBlobBytes(image,(MagickSizeType) image->offset) == MagickFalse)
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
    }
  /*
    Allocate memory for a scanline.
  */
  if (interlace == NoInterlace)
    scanline=(unsigned char *) AcquireQuantumMemory((size_t) (2UL*
      image->columns+2UL),(size_t) quantum*sizeof(*scanline));
  else
    scanline=(unsigned char *) AcquireQuantumMemory(image->columns,
      (size_t) quantum*sizeof(*scanline));
  if (scanline == (unsigned char *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  status=MagickTrue;
  do
  {
    chroma_image=CloneImage(image,(image->columns+horizontal_factor-1)/
      horizontal_factor,(image->rows+vertical_factor-1)/vertical_factor,
      MagickTrue,exception);
    if (chroma_image == (Image *) NULL)
      {
        scanline=(unsigned char *) RelinquishMagickMemory(scanline);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
    /*
      Convert raster image to pixel packets.
    */
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    status=SetImageExtent(image,image->columns,image->rows,exception);
    if (status == MagickFalse)
      break;
    if (interlace == PartitionInterlace)
      {
        AppendImageFormat("Y",image->filename);
        status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
        if (status == MagickFalse)
          {
            scanline=(unsigned char *) RelinquishMagickMemory(scanline);
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
      }
    for (y=0; y < (ssize_t) image->rows; y++)
    {
      register Quantum
        *chroma_pixels;

      if (interlace == NoInterlace)
        {
          if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
            {
              length=2*quantum*image->columns;
              count=ReadBlob(image,length,scanline);
              if (count != (ssize_t) length)
                {
                  status=MagickFalse;
                  ThrowFileException(exception,CorruptImageError,
                    "UnexpectedEndOfFile",image->filename);
                  break;
                }
            }
          p=scanline;
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (Quantum *) NULL)
            break;
          chroma_pixels=QueueAuthenticPixels(chroma_image,0,y,
            chroma_image->columns,1,exception);
          if (chroma_pixels == (Quantum *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x+=2)
          {
            SetPixelRed(chroma_image,0,chroma_pixels);
            if (quantum == 1)
              SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),
                chroma_pixels);
            else
              {
                SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
                  *(p+1)),chroma_pixels);
                p+=2;
              }
            if (quantum == 1)
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
            else
              {
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
                p+=2;
              }
            SetPixelGreen(image,0,q);
            SetPixelBlue(image,0,q);
            q+=GetPixelChannels(image);
            SetPixelGreen(image,0,q);
            SetPixelBlue(image,0,q);
            if (quantum == 1)
              SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),chroma_pixels);
            else
              {
                SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
                  *(p+1)),chroma_pixels);
                p+=2;
              }
            if (quantum == 1)
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
            else
              {
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
                p+=2;
              }
            chroma_pixels+=GetPixelChannels(chroma_image);
            q+=GetPixelChannels(image);
          }
        }
      else
        {
          if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
            {
              length=quantum*image->columns;
              count=ReadBlob(image,length,scanline);
              if (count != (ssize_t) length)
                {
                  status=MagickFalse;
                  ThrowFileException(exception,CorruptImageError,
                    "UnexpectedEndOfFile",image->filename);
                  break;
                }
            }
          p=scanline;
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (Quantum *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            if (quantum == 1)
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
            else
              {
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
                p+=2;
              }
            SetPixelGreen(image,0,q);
            SetPixelBlue(image,0,q);
            q+=GetPixelChannels(image);
          }
        }
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
        break;
      if (interlace == NoInterlace)
        if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
          break;
      if (image->previous == (Image *) NULL)
        {
          status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
            image->rows);
          if (status == MagickFalse)
            break;
        }
    }
    if (interlace == PartitionInterlace)
      {
        (void) CloseBlob(image);
        AppendImageFormat("U",image->filename);
        status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
        if (status == MagickFalse)
          {
            scanline=(unsigned char *) RelinquishMagickMemory(scanline);
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
      }
    if (interlace != NoInterlace)
      {
        for (y=0; y < (ssize_t) chroma_image->rows; y++)
        {
          length=quantum*chroma_image->columns;
          count=ReadBlob(image,length,scanline);
          if (count != (ssize_t) length)
            {
              status=MagickFalse;
              ThrowFileException(exception,CorruptImageError,
                "UnexpectedEndOfFile",image->filename);
              break;
            }
          p=scanline;
          q=QueueAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
            exception);
          if (q == (Quantum *) NULL)
            break;
          for (x=0; x < (ssize_t) chroma_image->columns; x++)
          {
            SetPixelRed(chroma_image,0,q);
            if (quantum == 1)
              SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),q);
            else
              {
                SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
                  *(p+1)),q);
                p+=2;
              }
            SetPixelBlue(chroma_image,0,q);
            q+=GetPixelChannels(chroma_image);
          }
          if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
            break;
        }
      if (interlace == PartitionInterlace)
        {
          (void) CloseBlob(image);
          AppendImageFormat("V",image->filename);
          status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
          if (status == MagickFalse)
            {
              scanline=(unsigned char *) RelinquishMagickMemory(scanline);
              image=DestroyImageList(image);
              return((Image *) NULL);
            }
        }
      for (y=0; y < (ssize_t) chroma_image->rows; y++)
      {
        length=quantum*chroma_image->columns;
        count=ReadBlob(image,length,scanline);
        if (count != (ssize_t) length)
          {
            status=MagickFalse;
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);
            break;
          }
        p=scanline;
        q=GetAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < (ssize_t) chroma_image->columns; x++)
        {
          if (quantum == 1)
            SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),q);
          else
            {
              SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
                *(p+1)),q);
              p+=2;
            }
          q+=GetPixelChannels(chroma_image);
        }
        if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
          break;
      }
    }
    /*
      Scale image.
    */
    resize_image=ResizeImage(chroma_image,image->columns,image->rows,
      TriangleFilter,exception);
    chroma_image=DestroyImage(chroma_image);
    if (resize_image == (Image *) NULL)
      {
        scanline=(unsigned char *) RelinquishMagickMemory(scanline);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
    for (y=0; y < (ssize_t) image->rows; y++)
    {
      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
      chroma_pixels=GetVirtualPixels(resize_image,0,y,resize_image->columns,1,
        exception);
      if ((q == (Quantum *) NULL) ||
          (chroma_pixels == (const Quantum *) NULL))
        break;
      for (x=0; x < (ssize_t) image->columns; x++)
      {
        SetPixelGreen(image,GetPixelGreen(resize_image,chroma_pixels),q);
        SetPixelBlue(image,GetPixelBlue(resize_image,chroma_pixels),q);
        chroma_pixels+=GetPixelChannels(resize_image);
        q+=GetPixelChannels(image);
      }
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
        break;
    }
    resize_image=DestroyImage(resize_image);
    if (SetImageColorspace(image,YCbCrColorspace,exception) == MagickFalse)
      break;
    if (interlace == PartitionInterlace)
      (void) CopyMagickString(image->filename,image_info->filename,
        MagickPathExtent);
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    if (interlace == NoInterlace)
      count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
    else
      count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
    if (count != 0)
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image,exception);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            scanline=(unsigned char *) RelinquishMagickMemory(scanline);
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
      }
  } while (count != 0);
  scanline=(unsigned char *) RelinquishMagickMemory(scanline);
  (void) CloseBlob(image);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  return(GetFirstImageInList(image));
}


PackLinuxElf64amd::PackLinuxElf64amd(InputFile *f)
    : super(f)
{
    e_machine = Elf64_Ehdr::EM_X86_64;
    ei_class = Elf64_Ehdr::ELFCLASS64;
    ei_data = Elf64_Ehdr::ELFDATA2LSB;
    ei_osabi  = Elf32_Ehdr::ELFOSABI_LINUX;
}


PackLinuxElf64ppcle::PackLinuxElf64ppcle(InputFile *f)
    : super(f), lg2_page(16), page_size(1u<<lg2_page)
{
    e_machine = Elf64_Ehdr::EM_PPC64;
    ei_class  = Elf64_Ehdr::ELFCLASS64;
    ei_data   = Elf64_Ehdr::ELFDATA2LSB;
    ei_osabi  = Elf64_Ehdr::ELFOSABI_LINUX;
}


Linker* PackLinuxElf32x86::newLinker() const
{
    return new ElfLinkerX86;
}


PackLinuxElf32armBe::getFilters() const
{
    return ARM_getFilters(true);
}


int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
{
    Extent x;
    unsigned k;
    bool const is_shlib = (0!=xct_off);

    // count passes, set ptload vars
    uip->ui_total_passes = 0;
    for (k = 0; k < e_phnum; ++k) {
        if (PT_LOAD64==get_te32(&phdri[k].p_type)) {
            uip->ui_total_passes++;
            if (find_LOAD_gap(phdri, k, e_phnum)) {
                uip->ui_total_passes++;
            }
        }
    }
    uip->ui_total_passes -= !!is_shlib;  // not .data of shlib

    // compress extents
    unsigned hdr_u_len = sizeof(Elf64_Ehdr) + sz_phdrs;

    unsigned total_in = xct_off - (is_shlib ? hdr_u_len : 0);
    unsigned total_out = xct_off;

    uip->ui_pass = 0;
    ft.addvalue = 0;

    int nx = 0;
    for (k = 0; k < e_phnum; ++k) if (PT_LOAD64==get_te32(&phdri[k].p_type)) {
        if (ft.id < 0x40) {
            // FIXME: ??    ft.addvalue = phdri[k].p_vaddr;
        }
        x.offset = get_te64(&phdri[k].p_offset);
        x.size   = get_te64(&phdri[k].p_filesz);
        if (0 == nx) { // 1st PT_LOAD64 must cover Ehdr at 0==p_offset
            unsigned const delta = !is_shlib
                ? (sizeof(Elf64_Ehdr) + sz_phdrs)  // main executable
                : xct_off;  // shared library
            if (ft.id < 0x40) {
                // FIXME: ??     ft.addvalue += delta;
            }
            x.offset += delta;
            x.size   -= delta;
        }
        // compressWithFilters() always assumes a "loader", so would
        // throw NotCompressible for small .data Extents, which PowerPC
        // sometimes marks as PF_X anyway.  So filter only first segment.
        if (0==nx || !is_shlib)
        packExtent(x, total_in, total_out,
            ((0==nx && (Elf64_Phdr::PF_X & get_te64(&phdri[k].p_flags)))
                ? &ft : 0 ), fo, hdr_u_len);
        else
            total_in += x.size;
        hdr_u_len = 0;
        ++nx;
    }
    sz_pack2a = fpad4(fo);  // MATCH01

    // Accounting only; ::pack3 will do the compression and output
    for (k = 0; k < e_phnum; ++k) { //
        total_in += find_LOAD_gap(phdri, k, e_phnum);
    }

    if ((off_t)total_in != file_size)
        throwEOFException();

    return 0;  // omit end-of-compression bhdr for now
}


PackLinuxElf32armBe::getCompressionMethods(int method, int level) const
{
    return Packer::getDefaultCompressionMethods_8(method, level);
}


PackLinuxElf32::checkEhdr(Elf32_Ehdr const *ehdr) const
{
    const unsigned char * const buf = ehdr->e_ident;

    if (0!=memcmp(buf, "\x7f\x45\x4c\x46", 4)  // "\177ELF"
    ||  buf[Elf32_Ehdr::EI_CLASS]!=ei_class
    ||  buf[Elf32_Ehdr::EI_DATA] !=ei_data
    ) {
        return -1;
    }
    if (!memcmp(buf+8, "FreeBSD", 7))                   // branded
        return 1;

    int const type = get_te16(&ehdr->e_type);
    if (type != Elf32_Ehdr::ET_EXEC && type != Elf32_Ehdr::ET_DYN)
        return 2;
    if (get_te16(&ehdr->e_machine) != (unsigned) e_machine)
        return 3;
    if (get_te32(&ehdr->e_version) != Elf32_Ehdr::EV_CURRENT)
        return 4;
    if (e_phnum < 1)
        return 5;
    if (get_te16(&ehdr->e_phentsize) != sizeof(Elf32_Phdr))
        return 6;

    if (type == Elf32_Ehdr::ET_EXEC) {
        // check for Linux kernels
        unsigned const entry = get_te32(&ehdr->e_entry);
        if (entry == 0xC0100000)    // uncompressed vmlinux
            return 1000;
        if (entry == 0x00001000)    // compressed vmlinux
            return 1001;
        if (entry == 0x00100000)    // compressed bvmlinux
            return 1002;
    }

    // FIXME: add more checks for kernels

    // FIXME: add special checks for other ELF i386 formats, like
    //        NetBSD, OpenBSD, Solaris, ....

    // success
    return 0;
}


PackOpenBSDElf32x86::generateElfHdr(
    OutputFile *fo,
    void const *proto,
    unsigned const brka
)
{
    cprElfHdr3 *const h3 = (cprElfHdr3 *)(void *)&elfout;
    memcpy(h3, proto, sizeof(*h3));  // reads beyond, but OK
    h3->ehdr.e_ident[Elf32_Ehdr::EI_OSABI] = ei_osabi;
    assert(2==get_te16(&h3->ehdr.e_phnum));
    set_te16(&h3->ehdr.e_phnum, 3);

    assert(get_te32(&h3->ehdr.e_phoff)     == sizeof(Elf32_Ehdr));
                         h3->ehdr.e_shoff = 0;
    assert(get_te16(&h3->ehdr.e_ehsize)    == sizeof(Elf32_Ehdr));
    assert(get_te16(&h3->ehdr.e_phentsize) == sizeof(Elf32_Phdr));
           set_te16(&h3->ehdr.e_shentsize, sizeof(Elf32_Shdr));
                         h3->ehdr.e_shnum = 0;
                         h3->ehdr.e_shstrndx = 0;

    struct {
        Elf32_Nhdr nhdr;
        char name[8];
        unsigned body;
    } elfnote;

    unsigned const note_offset = sizeof(*h3) - sizeof(linfo);
    sz_elf_hdrs = sizeof(elfnote) + note_offset;

    set_te32(&h3->phdr[2].p_type, Elf32_Phdr::PT_NOTE);
    set_te32(&h3->phdr[2].p_offset, note_offset);
    set_te32(&h3->phdr[2].p_vaddr, note_offset);
    set_te32(&h3->phdr[2].p_paddr, note_offset);
    set_te32(&h3->phdr[2].p_filesz, sizeof(elfnote));
    set_te32(&h3->phdr[2].p_memsz,  sizeof(elfnote));
    set_te32(&h3->phdr[2].p_flags, Elf32_Phdr::PF_R);
    set_te32(&h3->phdr[2].p_align, 4);

    // Q: Same as this->note_body[0 .. this->note_size-1] ?
    set_te32(&elfnote.nhdr.namesz, 8);
    set_te32(&elfnote.nhdr.descsz, OPENBSD_DESCSZ);
    set_te32(&elfnote.nhdr.type,   NHDR_OPENBSD_TAG);
    memcpy(elfnote.name, "OpenBSD", sizeof(elfnote.name));
    elfnote.body = 0;

    set_te32(&h3->phdr[0].p_filesz, sz_elf_hdrs);
              h3->phdr[0].p_memsz = h3->phdr[0].p_filesz;

    unsigned const brkb = brka | ((0==(~page_mask & brka)) ? 0x20 : 0);
    set_te32(&h3->phdr[1].p_type, PT_LOAD32);  // be sure
    set_te32(&h3->phdr[1].p_offset, ~page_mask & brkb);
    set_te32(&h3->phdr[1].p_vaddr, brkb);
    set_te32(&h3->phdr[1].p_paddr, brkb);
    h3->phdr[1].p_filesz = 0;
    h3->phdr[1].p_memsz =  0;
    set_te32(&h3->phdr[1].p_flags, Elf32_Phdr::PF_R | Elf32_Phdr::PF_W);

    if (ph.format==getFormat()) {
        memset(&h3->linfo, 0, sizeof(h3->linfo));
        fo->write(h3, sizeof(*h3) - sizeof(h3->linfo));
        fo->write(&elfnote, sizeof(elfnote));
        fo->write(&h3->linfo, sizeof(h3->linfo));
    }
    else {
        assert(false);  // unknown ph.format, PackLinuxElf32
    }
}


PackLinuxElf32mipsel::getFilters() const
{
    static const int f_none[] = { FT_END };
    return f_none;
}


PackNetBSDElf32x86::generateElfHdr(
    OutputFile *fo,
    void const *proto,
    unsigned const brka
)
{
    super::generateElfHdr(fo, proto, brka);
    cprElfHdr2 *const h2 = (cprElfHdr2 *)(void *)&elfout;

    sz_elf_hdrs = sizeof(*h2) - sizeof(linfo);
    unsigned note_offset = sz_elf_hdrs;

    // Find the NetBSD PT_NOTE and the PaX PT_NOTE.
    Elf32_Nhdr const *np_NetBSD = 0;  unsigned sz_NetBSD = 0;
    Elf32_Nhdr const *np_PaX    = 0;  unsigned sz_PaX    = 0;
    unsigned char *cp = note_body;
    unsigned j;
    for (j=0; j < note_size; ) {
        Elf32_Nhdr const *const np = (Elf32_Nhdr const *)(void *)cp;
        int k = sizeof(*np) + up4(get_te32(&np->namesz))
            + up4(get_te32(&np->descsz));

        if (NHDR_NETBSD_TAG == np->type && 7== np->namesz
        &&  NETBSD_DESCSZ == np->descsz
        &&  0==strcmp(ELF_NOTE_NETBSD_NAME,
                /* &np->body */ (char const *)(1+ np))) {
            np_NetBSD = np;
            sz_NetBSD = k;
        }
        if (NHDR_PAX_TAG == np->type && 4== np->namesz
        &&  PAX_DESCSZ==np->descsz
        &&  0==strcmp(ELF_NOTE_PAX_NAME,
                /* &np->body */ (char const *)(1+ np))) {
            np_PaX = np;
            sz_PaX = k;
        }
        cp += k;
        j += k;
    }

    // Add PT_NOTE for the NetBSD note and PaX note, if any.
    note_offset += (np_NetBSD ? sizeof(Elf32_Phdr) : 0);
    note_offset += (np_PaX    ? sizeof(Elf32_Phdr) : 0);
    Elf32_Phdr *phdr = &elfout.phdr[2];
    if (np_NetBSD) {
        set_te32(&phdr->p_type, Elf32_Phdr::PT_NOTE);
        set_te32(&phdr->p_offset, note_offset);
        set_te32(&phdr->p_vaddr, note_offset);
        set_te32(&phdr->p_paddr, note_offset);
        set_te32(&phdr->p_filesz, sz_NetBSD);
        set_te32(&phdr->p_memsz,  sz_NetBSD);
        set_te32(&phdr->p_flags, Elf32_Phdr::PF_R);
        set_te32(&phdr->p_align, 4);

        sz_elf_hdrs += sz_NetBSD + sizeof(*phdr);
        note_offset += sz_NetBSD;
        ++phdr;
    }
    if (np_PaX) {
        set_te32(&phdr->p_type, Elf32_Phdr::PT_NOTE);
        set_te32(&phdr->p_offset, note_offset);
        set_te32(&phdr->p_vaddr, note_offset);
        set_te32(&phdr->p_paddr, note_offset);
        set_te32(&phdr->p_filesz, sz_PaX);
        set_te32(&phdr->p_memsz,  sz_PaX);
        set_te32(&phdr->p_flags, Elf32_Phdr::PF_R);
        set_te32(&phdr->p_align, 4);

        /* &np_PaX->body[4] */
        const unsigned char *p4 =  &(ACC_CCAST(const unsigned char *, (1+ np_PaX)))[4];
        unsigned bits = get_te32(p4);
        bits &= ~PAX_MPROTECT;
        bits |=  PAX_NOMPROTECT;
        set_te32(ACC_UNCONST_CAST(unsigned char *, p4), bits);

        sz_elf_hdrs += sz_PaX + sizeof(*phdr);
        note_offset += sz_PaX;
        ++phdr;
    }
    set_te32(&h2->phdr[0].p_filesz, note_offset);
              h2->phdr[0].p_memsz = h2->phdr[0].p_filesz;

    if (ph.format==getFormat()) {
        set_te16(&h2->ehdr.e_phnum, !!sz_NetBSD + !!sz_PaX +
        get_te16(&h2->ehdr.e_phnum));
        fo->seek(0, SEEK_SET);
        fo->rewrite(h2, sizeof(*h2) - sizeof(h2->linfo));

        // The 'if' guards on these two calls to memcpy are required
        // because the C Standard Committee did not debug the Standard
        // before publishing.  An empty region (0==size) must nevertheless
        // have a valid (non-NULL) pointer.
        if (sz_NetBSD) memcpy(&((char *)phdr)[0],         np_NetBSD, sz_NetBSD);
        if (sz_PaX)    memcpy(&((char *)phdr)[sz_NetBSD], np_PaX,    sz_PaX);

        fo->write(&elfout.phdr[2],
            &((char *)phdr)[sz_PaX + sz_NetBSD] - (char *)&elfout.phdr[2]);

        l_info foo; memset(&foo, 0, sizeof(foo));
        fo->rewrite(&foo, sizeof(foo));
    }
    else {
        assert(false);  // unknown ph.format, PackLinuxElf32
    }
}


PackLinuxElf64::canPack()
{
    union {
        unsigned char buf[sizeof(Elf64_Ehdr) + 14*sizeof(Elf64_Phdr)];
        //struct { Elf64_Ehdr ehdr; Elf64_Phdr phdr; } e;
    } u;
    COMPILE_TIME_ASSERT(sizeof(u) <= 1024)

    fi->readx(u.buf, sizeof(u.buf));
    fi->seek(0, SEEK_SET);
    Elf64_Ehdr const *const ehdr = (Elf64_Ehdr *) u.buf;

    // now check the ELF header
    if (checkEhdr(ehdr) != 0)
        return false;

    // additional requirements for linux/elf386
    if (get_te16(&ehdr->e_ehsize) != sizeof(*ehdr)) {
        throwCantPack("invalid Ehdr e_ehsize; try '--force-execve'");
        return false;
    }
    if (e_phoff != sizeof(*ehdr)) {// Phdrs not contiguous with Ehdr
        throwCantPack("non-contiguous Ehdr/Phdr; try '--force-execve'");
        return false;
    }

    // The first PT_LOAD64 must cover the beginning of the file (0==p_offset).
    Elf64_Phdr const *phdr = phdri;
    for (unsigned j=0; j < e_phnum; ++phdr, ++j) {
        if (j >= 14)
            return false;
        unsigned const p_type = get_te32(&phdr->p_type);
        if (1!=exetype && phdr->PT_LOAD64 == p_type) { // 1st PT_LOAD
            exetype = 1;
            load_va = get_te64(&phdr->p_vaddr);  // class data member
            upx_uint64_t const p_offset = get_te64(&phdr->p_offset);
            upx_uint64_t const off = ~page_mask & load_va;
            if (off && off == p_offset) { // specific hint
                throwCantPack("Go-language PT_LOAD: try hemfix.c, or try '--force-execve'");
                // Fixing it inside upx fails because packExtent() reads original file.
                return false;
            }
            if (0 != p_offset) { // 1st PT_LOAD must cover Ehdr and Phdr
                throwCantPack("first PT_LOAD.p_offset != 0; try '--force-execve'");
                return false;
            }
            hatch_off = phdr->p_memsz;
            break;
        }
    }
    // We want to compress position-independent executable (gcc -pie)
    // main programs, but compressing a shared library must be avoided
    // because the result is no longer usable.  In theory, there is no way
    // to tell them apart: both are just ET_DYN.  Also in theory,
    // neither the presence nor the absence of any particular symbol name
    // can be used to tell them apart; there are counterexamples.
    // However, we will use the following heuristic suggested by
    // Peter S. Mazinger <ps.m@gmx.net> September 2005:
    // If a ET_DYN has __libc_start_main as a global undefined symbol,
    // then the file is a position-independent executable main program
    // (that depends on libc.so.6) and is eligible to be compressed.
    // Otherwise (no __libc_start_main as global undefined): skip it.
    // Also allow  __uClibc_main  and  __uClibc_start_main .

    if (Elf64_Ehdr::ET_DYN==get_te16(&ehdr->e_type)) {
        // The DT_SYMTAB has no designated length.  Read the whole file.
        alloc_file_image(file_image, file_size);
        fi->seek(0, SEEK_SET);
        fi->readx(file_image, file_size);
        memcpy(&ehdri, ehdr, sizeof(Elf64_Ehdr));
        phdri= (Elf64_Phdr       *)((size_t)e_phoff + file_image);  // do not free() !!
        shdri= (Elf64_Shdr const *)((size_t)e_shoff + file_image);  // do not free() !!

        //sec_strndx = &shdri[ehdr->e_shstrndx];
        //shstrtab = (char const *)(sec_strndx->sh_offset + file_image);
        sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM);
        if (sec_dynsym)
            sec_dynstr = get_te64(&sec_dynsym->sh_link) + shdri;

        int j= e_phnum;
        phdr= phdri;
        for (; --j>=0; ++phdr)
        if (Elf64_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
            dynseg= (Elf64_Dyn const *)(get_te64(&phdr->p_offset) + file_image);
            break;
        }
        // elf_find_dynamic() returns 0 if 0==dynseg.
        dynstr=          (char const *)elf_find_dynamic(Elf64_Dyn::DT_STRTAB);
        dynsym=     (Elf64_Sym const *)elf_find_dynamic(Elf64_Dyn::DT_SYMTAB);

        if (Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1)
        ||  calls_crt1((Elf64_Rela const *)elf_find_dynamic(Elf64_Dyn::DT_RELA),
                                  (int)elf_unsigned_dynamic(Elf64_Dyn::DT_RELASZ))
        ||  calls_crt1((Elf64_Rela const *)elf_find_dynamic(Elf64_Dyn::DT_JMPREL),
                                  (int)elf_unsigned_dynamic(Elf64_Dyn::DT_PLTRELSZ))) {
            is_pie = true;
            goto proceed;  // calls C library init for main program
        }

        // Heuristic HACK for shared libraries (compare Darwin (MacOS) Dylib.)
        // If there is an existing DT_INIT, and if everything that the dynamic
        // linker ld-linux needs to perform relocations before calling DT_INIT
        // resides below the first SHT_EXECINSTR Section in one PT_LOAD, then
        // compress from the first executable Section to the end of that PT_LOAD.
        // We must not alter anything that ld-linux might touch before it calls
        // the DT_INIT function.
        //
        // Obviously this hack requires that the linker script put pieces
        // into good positions when building the original shared library,
        // and also requires ld-linux to behave.

        if (elf_find_dynamic(Elf64_Dyn::DT_INIT)) {
            if (elf_has_dynamic(Elf64_Dyn::DT_TEXTREL)) {
                throwCantPack("DT_TEXTREL found; re-compile with -fPIC");
                goto abandon;
            }
            Elf64_Shdr const *shdr = shdri;
            xct_va = ~0ull;
            for (j= e_shnum; --j>=0; ++shdr) {
                if (Elf64_Shdr::SHF_EXECINSTR & get_te32(&shdr->sh_flags)) {
                    xct_va = umin64(xct_va, get_te64(&shdr->sh_addr));
                }
            }
            // Rely on 0==elf_unsigned_dynamic(tag) if no such tag.
            upx_uint64_t const va_gash = elf_unsigned_dynamic(Elf64_Dyn::DT_GNU_HASH);
            upx_uint64_t const va_hash = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);
            if (xct_va < va_gash  ||  (0==va_gash && xct_va < va_hash)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_STRTAB)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_SYMTAB)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_REL)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_RELA)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_JMPREL)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERDEF)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERSYM)
            ||  xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERNEEDED) ) {
                throwCantPack("DT_ tag above stub");
                goto abandon;
            }
            for ((shdr= shdri), (j= e_shnum); --j>=0; ++shdr) {
                upx_uint64_t const sh_addr = get_te64(&shdr->sh_addr);
                if ( sh_addr==va_gash
                ||  (sh_addr==va_hash && 0==va_gash) ) {
                    shdr= &shdri[get_te32(&shdr->sh_link)];  // the associated SHT_SYMTAB
                    //hatch_off = (char *)&ehdri.e_ident[11] - (char *)&ehdri;
                    break;
                }
            }
            ACC_UNUSED(shdr);
            xct_off = elf_get_offset_from_address(xct_va);
            goto proceed;  // But proper packing depends on checking xct_va.
        }
        else
            throwCantPack("need DT_INIT; try \"void _init(void){}\"");
abandon:
        return false;
proceed: ;
    }
    // XXX Theoretically the following test should be first,
    // but PackUnix::canPack() wants 0!=exetype ?
    if (!super::canPack())
        return false;
    assert(exetype == 1);

    exetype = 0;

    // set options
    opt->o_unix.blocksize = blocksize = file_size;
    return true;
}


Linker* PackLinuxElf32armBe::newLinker() const
{
    return new ElfLinkerArmBE();
}


void PackLinuxElf64::pack3(OutputFile *fo, Filter &ft)
{
    super::pack3(fo, ft);  // loader follows compressed PT_LOADs
    // Then compressed gaps (including debuginfo.)
    unsigned total_in = 0, total_out = 0;
    for (unsigned k = 0; k < e_phnum; ++k) {
        Extent x;
        x.size = find_LOAD_gap(phdri, k, e_phnum);
        if (x.size) {
            x.offset = get_te64(&phdri[k].p_offset) +
                       get_te64(&phdri[k].p_filesz);
            packExtent(x, total_in, total_out, 0, fo);
        }
    }
    // write block end marker (uncompressed size 0)
    b_info hdr; memset(&hdr, 0, sizeof(hdr));
    set_le32(&hdr.sz_cpr, UPX_MAGIC_LE32);
    fo->write(&hdr, sizeof(hdr));
    fpad4(fo);

    set_te64(&elfout.phdr[0].p_filesz, sz_pack2 + lsize);
    set_te64(&elfout.phdr[0].p_memsz,  sz_pack2 + lsize);
    if (0!=xct_off) {  // shared library
        Elf64_Phdr *phdr = phdri;
        unsigned off = fo->st_size();
        unsigned off_init = 0;  // where in file
        upx_uint64_t va_init = sz_pack2;   // virtual address
        so_slide = 0;
        for (int j = e_phnum; --j>=0; ++phdr) {
            upx_uint64_t const len  = get_te64(&phdr->p_filesz);
            upx_uint64_t const ioff = get_te64(&phdr->p_offset);
            upx_uint64_t       align= get_te64(&phdr->p_align);
            unsigned const type = get_te32(&phdr->p_type);
            if (phdr->PT_INTERP==type) {
                // Rotate to highest position, so it can be lopped
                // by decrementing e_phnum.
                memcpy((unsigned char *)ibuf, phdr, sizeof(*phdr));
                memmove(phdr, 1+phdr, j * sizeof(*phdr));  // overlapping
                memcpy(&phdr[j], (unsigned char *)ibuf, sizeof(*phdr));
                --phdr;
                set_te16(&ehdri.e_phnum, --e_phnum);
                continue;
            }
            if (phdr->PT_LOAD==type) {
                if (xct_off < ioff) {  // Slide up non-first PT_LOAD.
                    // AMD64 chip supports page sizes of 4KiB, 2MiB, and 1GiB;
                    // the operating system chooses one.  .p_align typically
                    // is a forward-looking 2MiB.  In 2009 Linux chooses 4KiB.
                    // We choose 4KiB to waste less space.  If Linux chooses
                    // 2MiB later, then our output will not run.
                    if ((1u<<12) < align) {
                        align = 1u<<12;
                        set_te64(&phdr->p_align, align);
                    }
                    off += (align-1) & (ioff - off);
                    fi->seek(ioff, SEEK_SET); fi->readx(ibuf, len);
                    fo->seek( off, SEEK_SET); fo->write(ibuf, len);
                    so_slide = off - ioff;
                    set_te64(&phdr->p_offset, so_slide + ioff);
                }
                else {  // Change length of first PT_LOAD.
                    va_init += get_te64(&phdr->p_vaddr);
                    set_te64(&phdr->p_filesz, sz_pack2 + lsize);
                    set_te64(&phdr->p_memsz,  sz_pack2 + lsize);
                }
                continue;  // all done with this PT_LOAD
            }
            // Compute new offset of &DT_INIT.d_val.
            if (phdr->PT_DYNAMIC==type) {
                off_init = so_slide + ioff;
                fi->seek(ioff, SEEK_SET);
                fi->read(ibuf, len);
                Elf64_Dyn *dyn = (Elf64_Dyn *)(void *)ibuf;
                for (int j2 = len; j2 > 0; ++dyn, j2 -= sizeof(*dyn)) {
                    if (dyn->DT_INIT==get_te64(&dyn->d_tag)) {
                        unsigned const t = (unsigned char *)&dyn->d_val -
                                           (unsigned char *)ibuf;
                        off_init += t;
                        break;
                    }
                }
                // fall through to relocate .p_offset
            }
            if (xct_off < ioff)
                set_te64(&phdr->p_offset, so_slide + ioff);
        }
        if (off_init) {  // change DT_INIT.d_val
            fo->seek(off_init, SEEK_SET);
            upx_uint64_t word; set_te64(&word, va_init);
            fo->rewrite(&word, sizeof(word));
            fo->seek(0, SEEK_END);
        }
        ehdri.e_shnum = 0;
        ehdri.e_shoff = 0;
        ehdri.e_shstrndx = 0;
    }
}


ARM_getFilters(bool const isBE)
{
    static const int f50[] = { 0x50, FT_END };
    static const int f51[] = { 0x51, FT_END };
    if (isBE)
        return f51;
    return f50;
}


PackLinuxElf32x86::PackLinuxElf32x86(InputFile *f) : super(f)
{
    e_machine = Elf32_Ehdr::EM_386;
    ei_class  = Elf32_Ehdr::ELFCLASS32;
    ei_data   = Elf32_Ehdr::ELFDATA2LSB;
    ei_osabi  = Elf32_Ehdr::ELFOSABI_LINUX;
}


void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
{
    overlay_offset = sz_elf_hdrs + sizeof(linfo);

    if (opt->o_unix.preserve_build_id) {
        // calc e_shoff here and write shdrout, then o_shstrtab
        //NOTE: these are pushed last to ensure nothing is stepped on
        //for the UPX structure.
        unsigned const len = fpad4(fo);
        set_te64(&elfout.ehdr.e_shoff,len);

        int const ssize = sizeof(shdrout);

        shdrout.shdr[2].sh_offset = len+ssize;
        shdrout.shdr[1].sh_offset = shdrout.shdr[2].sh_offset+shdrout.shdr[2].sh_size;

        fo->write(&shdrout, ssize);

        fo->write(o_shstrtab,shdrout.shdr[2].sh_size);
        fo->write(buildid_data,shdrout.shdr[1].sh_size);
    }

    if (0!=xct_off) {  // shared library
        if (opt->o_unix.android_shlib && shdri) { // dlopen() checks Elf64_Shdr vs Elf64_Phdr
            unsigned load0_hi = ~0u;
            Elf64_Phdr const *phdr = phdri;
            for (unsigned j = 0; j < e_phnum; ++j, ++phdr) {
                upx_uint64_t load0_lo = get_te64(&phdr->p_vaddr);
                upx_uint64_t load0_sz = get_te64(&phdr->p_memsz);
                if (PT_LOAD64==get_te32(&phdr->p_type)
                && (xct_off -  load0_lo) < load0_sz) {
                    load0_hi = load0_lo  + load0_sz;
                    break;
                }
            }
            MemBuffer smap(e_shnum); smap.clear();  // smap[0] = 0;  // SHN_UNDEF
            MemBuffer snew(e_shnum * sizeof(*shdri));
            Elf64_Shdr const *shdr = shdri;
            unsigned k = 0;
            for (unsigned j = 0; j < e_shnum; ++j, ++shdr) {
                // Select some Elf64_Shdr by .sh_type
                unsigned const type = get_te32(&shdr->sh_type);
                unsigned const flags = get_te32(&shdr->sh_flags);
                if (((Elf64_Shdr::SHT_STRTAB  == type) && (Elf64_Shdr::SHF_ALLOC & flags))
                ||  ((Elf64_Shdr::SHT_DYNAMIC == type) && (Elf64_Shdr::SHF_ALLOC & flags))
                ||  ((Elf64_Shdr::SHT_ARM_ATTRIBUTES == type))
                ||  (  ((1+ Elf64_Shdr::SHT_LOOS) <= type)  // SHT_ANDROID_REL
                    && ((2+ Elf64_Shdr::SHT_LOOS) >= type)) // SHT_ANDROID_RELA
                ) {
                    Elf64_Shdr *const sptr = k + (Elf64_Shdr *)(void *)snew;
                    upx_uint64_t va = get_te64(&shdr->sh_addr);
                    if (xct_off <= va && va <= load0_hi) {
                        throwCantPack("Android-required Shdr in packed region");
                    }
                    *sptr = *shdr;  // copy old Elf64_Shdr
                    smap[j] = 1+ k++;  // for later forwarding
                }
            }
            if (k && fo) {
                unsigned long const new_shoff = fpad8(fo);
                unsigned long xtra_off = ((1+ k) * sizeof(*shdri)) + new_shoff;  // 1+: shdr_undef
                set_te64(&ehdri.e_shoff, new_shoff);
                set_te16(&ehdri.e_shentsize, sizeof(*shdri));
                set_te16(&ehdri.e_shnum, 1+ k);
                Elf64_Shdr shdr_undef; memset(&shdr_undef, 0, sizeof(shdr_undef));
                fo->write(&shdr_undef, sizeof(shdr_undef));

                unsigned long arm_attr_offset = 0;
                unsigned long arm_attr_size = 0;
                for (unsigned j = 0; j < k; ++j) { // forward .sh_link
                    Elf64_Shdr *const sptr = j + (Elf64_Shdr *)(void *)snew;
                    // work-around for https://bugs.launchpad.net/bugs/1712938
                    if (Elf64_Shdr::SHT_ARM_ATTRIBUTES == get_te32(&sptr->sh_type)) {
                        arm_attr_offset = get_te64(&sptr->sh_offset);
                        arm_attr_size   = get_te64(&sptr->sh_size);
                        set_te64(&sptr->sh_offset, xtra_off);
                        xtra_off += get_te64(&sptr->sh_size);
                    }
                    upx_uint64_t sh_offset = get_te64(&sptr->sh_offset);
                    if (xct_off <= sh_offset) {
                        set_te64(&sptr->sh_offset, so_slide + sh_offset);
                    }
                    sptr->sh_name = 0;  // we flushed .e_shstrndx
                    set_te16(&sptr->sh_link, smap[sptr->sh_link]);
                    set_te16(&sptr->sh_info, smap[sptr->sh_info]);  // ?
                }
                fo->write(snew, k * sizeof(*shdri));
                if (arm_attr_offset) {
                    fo->write(&file_image[arm_attr_offset], arm_attr_size);
                }
            }
        }
    }
    // Cannot pre-round .p_memsz.  If .p_filesz < .p_memsz, then kernel
    // tries to make .bss, which requires PF_W.
    // But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
    set_te64(&elfout.phdr[0].p_filesz, sz_pack2 + lsize);
              elfout.phdr[0].p_memsz = elfout.phdr[0].p_filesz;
    super::pack4(fo, ft);  // write PackHeader and overlay_offset

    fo->seek(0, SEEK_SET);
    if (0!=xct_off) {  // shared library
        fo->rewrite(&ehdri, sizeof(ehdri));
        fo->rewrite(phdri, e_phnum * sizeof(*phdri));
        fo->seek(xct_off, SEEK_SET);
        fo->rewrite(&linfo, sizeof(linfo));
    }
    else {
        if (Elf64_Phdr::PT_NOTE==get_te64(&elfout.phdr[2].p_type)) {
            upx_uint64_t const reloc = get_te64(&elfout.phdr[0].p_vaddr);
            set_te64(            &elfout.phdr[2].p_vaddr,
                reloc + get_te64(&elfout.phdr[2].p_vaddr));
            set_te64(            &elfout.phdr[2].p_paddr,
                reloc + get_te64(&elfout.phdr[2].p_paddr));
            fo->rewrite(&elfout, sz_elf_hdrs);
            // FIXME   fo->rewrite(&elfnote, sizeof(elfnote));
        }
        else {
            fo->rewrite(&elfout, sz_elf_hdrs);
        }
        fo->rewrite(&linfo, sizeof(linfo));
    }
}


static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
					 void *arg)
{
	struct snd_seq_queue_info *info = arg;
	struct snd_seq_queue *q;

	q = snd_seq_queue_find_name(info->name);
	if (q == NULL)
		return -EINVAL;
	info->queue = q->queue;
	info->owner = q->owner;
	info->locked = q->locked;
	queuefree(q);

	return 0;
}


int snd_seq_port_disconnect(struct snd_seq_client *connector,
			    struct snd_seq_client *src_client,
			    struct snd_seq_client_port *src_port,
			    struct snd_seq_client *dest_client,
			    struct snd_seq_client_port *dest_port,
			    struct snd_seq_port_subscribe *info)
{
	struct snd_seq_port_subs_info *src = &src_port->c_src;
	struct snd_seq_subscribers *subs;
	int err = -ENOENT;

	down_write(&src->list_mutex);
	/* look for the connection */
	list_for_each_entry(subs, &src->list_head, src_list) {
		if (match_subs_info(info, &subs->info)) {
			atomic_dec(&subs->ref_count); /* mark as not ready */
			err = 0;
			break;
		}
	}
	up_write(&src->list_mutex);
	if (err < 0)
		return err;

	delete_and_unsubscribe_port(src_client, src_port, subs, true,
				    connector->number != src_client->number);
	delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
				    connector->number != dest_client->number);
	kfree(subs);
	return 0;
}


static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
					  void *arg)
{
	struct snd_seq_queue_status *status = arg;
	struct snd_seq_queue *queue;
	struct snd_seq_timer *tmr;

	queue = queueptr(status->queue);
	if (queue == NULL)
		return -EINVAL;
	memset(status, 0, sizeof(*status));
	status->queue = queue->queue;

	tmr = queue->timer;
	status->events = queue->tickq->cells + queue->timeq->cells;

	status->time = snd_seq_timer_get_cur_time(tmr);
	status->tick = snd_seq_timer_get_cur_tick(tmr);

	status->running = tmr->running;

	status->flags = queue->flags;
	queuefree(queue);

	return 0;
}


int snd_seq_event_port_attach(int client,
			      struct snd_seq_port_callback *pcbp,
			      int cap, int type, int midi_channels,
			      int midi_voices, char *portname)
{
	struct snd_seq_port_info portinfo;
	int  ret;

	/* Set up the port */
	memset(&portinfo, 0, sizeof(portinfo));
	portinfo.addr.client = client;
	strlcpy(portinfo.name, portname ? portname : "Unamed port",
		sizeof(portinfo.name));

	portinfo.capability = cap;
	portinfo.type = type;
	portinfo.kernel = pcbp;
	portinfo.midi_channels = midi_channels;
	portinfo.midi_voices = midi_voices;

	/* Create it */
	ret = snd_seq_kernel_client_ctl(client,
					SNDRV_SEQ_IOCTL_CREATE_PORT,
					&portinfo);

	if (ret >= 0)
		ret = portinfo.addr.port;

	return ret;
}


xmlMemDisplay(FILE *fp)
{
#ifdef MEM_LIST
    MEMHDR *p;
    unsigned idx;
    int     nb = 0;
#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
    time_t currentTime;
    char buf[500];
    struct tm * tstruct;
#endif
#endif
    FILE *old_fp = fp;

    if (fp == NULL) {
	fp = fopen(".memorylist", "w");
	if (fp == NULL)
	    return;
    }

#ifdef MEM_LIST
#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
    currentTime = time(NULL);
    tstruct = localtime(&currentTime);
    strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p", tstruct);
    fprintf(fp,"      %s\n\n", buf);
#endif

    fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
            debugMemSize, debugMaxMemSize);
    fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
    idx = 0;
    xmlMutexLock(xmlMemMutex);
    p = memlist;
    while (p) {
	  fprintf(fp,"%-5u  %6lu %6lu ",idx++,p->mh_number,
		  (unsigned long)p->mh_size);
        switch (p->mh_type) {
           case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
           case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
           case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
           case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
           case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
           default:
	        fprintf(fp,"Unknown memory block, may be corrupted");
		xmlMutexUnlock(xmlMemMutex);
		if (old_fp == NULL)
		    fclose(fp);
		return;
        }
	if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
        if (p->mh_tag != MEMTAG)
	      fprintf(fp,"  INVALID");
        nb++;
	if (nb < 100)
	    xmlMemContentShow(fp, p);
	else
	    fprintf(fp," skip");

        fprintf(fp,"\n");
        p = p->mh_next;
    }
    xmlMutexUnlock(xmlMemMutex);
#else
    fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
#endif
    if (old_fp == NULL)
	fclose(fp);
}


xmlMemShow(FILE *fp, int nr ATTRIBUTE_UNUSED)
{
#ifdef MEM_LIST
    MEMHDR *p;
#endif

    if (fp != NULL)
	fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
		debugMemSize, debugMaxMemSize);
#ifdef MEM_LIST
    xmlMutexLock(xmlMemMutex);
    if (nr > 0) {
	fprintf(fp,"NUMBER   SIZE  TYPE   WHERE\n");
	p = memlist;
	while ((p) && nr > 0) {
	      fprintf(fp,"%6lu %6lu ",p->mh_number,(unsigned long)p->mh_size);
	    switch (p->mh_type) {
	       case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
	       case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
	       case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
	      case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
	      case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
		default:fprintf(fp,"   ???    in ");break;
	    }
	    if (p->mh_file != NULL)
	        fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
	    if (p->mh_tag != MEMTAG)
		fprintf(fp,"  INVALID");
	    xmlMemContentShow(fp, p);
	    fprintf(fp,"\n");
	    nr--;
	    p = p->mh_next;
	}
    }
    xmlMutexUnlock(xmlMemMutex);
#endif /* MEM_LIST */
}


static void debugmem_list_add(MEMHDR *p)
{
     p->mh_next = memlist;
     p->mh_prev = NULL;
     if (memlist) memlist->mh_prev = p;
     memlist = p;
#ifdef MEM_LIST_DEBUG
     if (stderr)
     Mem_Display(stderr);
#endif
}


xmlMemStrdupLoc(const char *str, const char *file, int line)
{
    char *s;
    size_t size = strlen(str) + 1;
    MEMHDR *p;

    if (!xmlMemInitialized) xmlInitMemory();
    TEST_POINT

    if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
	xmlGenericError(xmlGenericErrorContext,
		"xmlMallocLoc : Unsigned overflow\n");
	xmlMemoryDump();
	return(NULL);
    }

    p = (MEMHDR *) malloc(RESERVE_SIZE+size);
    if (!p) {
      goto error;
    }
    p->mh_tag = MEMTAG;
    p->mh_size = size;
    p->mh_type = STRDUP_TYPE;
    p->mh_file = file;
    p->mh_line = line;
    xmlMutexLock(xmlMemMutex);
    p->mh_number = ++block;
    debugMemSize += size;
    debugMemBlocks++;
    if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
#ifdef MEM_LIST
    debugmem_list_add(p);
#endif
    xmlMutexUnlock(xmlMemMutex);

    s = (char *) HDR_2_CLIENT(p);

    if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();

    strcpy(s,str);

    TEST_POINT

    if (xmlMemTraceBlockAt == s) {
	xmlGenericError(xmlGenericErrorContext,
			"%p : Strdup() Ok\n", xmlMemTraceBlockAt);
	xmlMallocBreakpoint();
    }

    return(s);

error:
    return(NULL);
}


Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {}


  virtual inline void print(String *str, enum_query_type query_type)
  {
    str->append(func_name);
  }


  Item_direct_view_ref(THD *thd, Item_direct_ref *item)
    :Item_direct_ref(thd, item) {}


  virtual const Send_field *get_out_param_info() const
  { return NULL; }


  Item_cache_real(): Item_cache(),
    value(0) {}


table_map used_tables() const { return used_table_map; }


Item *copy_or_same(THD *thd) { return this; }


  bool walk(Item_processor processor, bool walk_subquery, uchar *args)
  {
    return arg->walk(processor, walk_subquery, args) ||
	    (this->*processor)(args);
  }


  void set(CHARSET_INFO *collation_arg,
           Derivation derivation_arg,
           uint repertoire_arg)
  {
    collation= collation_arg;
    derivation= derivation_arg;
    repertoire= repertoire_arg;
  }


void set_result_field(Field *field)	{ result_field= field; }


  Item_trigger_field(Name_resolution_context *context_arg,
                     row_version_type row_ver_arg,
                     const char *field_name_arg,
                     ulong priv, const bool ro)
    :Item_field(context_arg,
               (const char *)NULL, (const char *)NULL, field_name_arg),
     row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv),
     want_privilege(priv), table_grants(NULL), read_only (ro)
  {}


  const char *derivation_name() const
  {
    switch(derivation)
    {
      case DERIVATION_NUMERIC:   return "NUMERIC";
      case DERIVATION_IGNORABLE: return "IGNORABLE";
      case DERIVATION_COERCIBLE: return "COERCIBLE";
      case DERIVATION_IMPLICIT:  return "IMPLICIT";
      case DERIVATION_SYSCONST:  return "SYSCONST";
      case DERIVATION_EXPLICIT:  return "EXPLICIT";
      case DERIVATION_NONE:      return "NONE";
      default: return "UNKNOWN";
    }
  }


enum Item_result result_type() const { return ROW_RESULT; }


enum Item_result result_type() const { return STRING_RESULT; }


  double val_real()
  {
    illegal_method_call((const char*)"val");
    return 0;
  };


  Item_string(const char *str,uint length,
              CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
              uint repertoire= MY_REPERTOIRE_UNICODE30)
    : m_cs_specified(FALSE)
  {
    str_value.set_or_copy_aligned(str, length, cs);
    collation.set(cs, dv, repertoire);
    /*
      We have to have a different max_length than 'length' here to
      ensure that we get the right length if we do use the item
      to create a new table. In this case max_length must be the maximum
      number of chars for a string of this type because we in Create_field::
      divide the max_length with mbmaxlen).
    */
    max_length= str_value.numchars()*cs->mbmaxlen;
    set_name(str, length, cs);
    decimals=NOT_FIXED_DEC;
    // it is constant => can be used without fix_fields (and frequently used)
    fixed= 1;
    /*
      Check if the string has any character that can't be
      interpreted using the relevant charset.
    */
    check_well_formed_result(&str_value, false, false);
  }


  Item_return_int(const char *name_arg, uint length,
		  enum_field_types field_type_arg, longlong value= 0)
    :Item_int(name_arg, value, length), int_field_type(field_type_arg)
  {
    unsigned_flag=1;
  }


  DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg)
  {
    collation= collation_arg;
    derivation= derivation_arg;
    set_repertoire_from_charset(collation_arg);
  }


Item_copy_string (Item *item) : Item_copy(item) {}


  static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
  { return alloc_root(mem_root, size); }


Cached_item_int(Item *item_par) :item(item_par),value(0) {}


  virtual bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate)
  { return get_date(ltime,fuzzydate); }


enum Type type() const { return STRING_ITEM; }


  void set_str_with_copy(const char *str_arg, uint length_arg)
  {
    str_value.copy(str_arg, length_arg, collation.collation);
    max_length= str_value.numchars() * collation.collation->mbmaxlen;
  }


  virtual Field::geometry_type get_geometry_type() const
    { return Field::GEOM_GEOMETRY; };


inline enum Item::Type Item_splocal::type() const
{
  return m_type;
}


virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }


  bool eq_def(Field *field)
  {
    return cached_field ? cached_field->eq_def (field) : FALSE;
  }


enum_field_types field_type() const   { return MYSQL_TYPE_NULL; }


double val_real() { DBUG_ASSERT(fixed == 1); return value; }


  Item_default_value(Name_resolution_context *context_arg, Item *a)
    :Item_field(context_arg, (const char *)NULL, (const char *)NULL,
                (const char *)NULL),
     arg(a) {}


virtual Item* element_index(uint i) { return this; }


  Item_direct_view_ref(TABLE_LIST *view_arg, Item **item,
                       const char *field_name_arg)
    :Item_direct_ref(view_arg, item, field_name_arg)
  {}


agg_item_charsets_for_string_result_with_comparison(DTCollation &c,
                                                    const char *name,
                                                    Item **items, uint nitems,
                                                    int item_sep= 1)
{
  uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
              MY_COLL_ALLOW_COERCIBLE_CONV |
              MY_COLL_ALLOW_NUMERIC_CONV |
              MY_COLL_DISALLOW_NONE;
  return agg_item_charsets(c, name, items, nitems, flags, item_sep);
}


  bool get_time(MYSQL_TIME *ltime)
  {
    DBUG_ASSERT(fixed);
    return (*ref)->get_time(ltime);
  }


  virtual bool cache_const_expr_analyzer(uchar **arg)
  {
    // Item_name_const always wraps a literal, so there is no need to cache it.
    return false;
  }


Item *clone_item() { return new Item_int(name,value,max_length); }


  virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const
  { buf->set(val->integer, &my_charset_bin); return buf;}


virtual Item *equal_fields_propagator(uchar * arg) { return this; }


  virtual enum_monotonicity_info get_monotonicity_info() const
  { return NON_MONOTONIC; }


virtual double  val_result() { return val_real(); }


static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
{
  partition_info *part_info= lpt->part_info;
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
  char path[FN_REFLEN + 1];
  char shadow_path[FN_REFLEN + 1];
  DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
  uint next_entry= 0;
  DBUG_ENTER("write_log_final_change_partition");

  /*
    Do not link any previous log entry.
    Replace the revert operations with forced retry operations.
  */
  part_info->first_log_entry= NULL;
  build_table_filename(path, sizeof(path) - 1, lpt->db,
                       lpt->table_name, "", 0);
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
  mysql_mutex_lock(&LOCK_gdl);
  if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
    goto error;
  if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
                      lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION))
    goto error;
  if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE))
    goto error;
  log_entry= part_info->first_log_entry;
  part_info->frm_log_entry= log_entry;
  /* Overwrite the revert execute log entry with this retry execute entry */
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
  release_part_info_log_entries(old_first_log_entry);
  mysql_mutex_unlock(&LOCK_gdl);
  DBUG_RETURN(FALSE);

error:
  release_part_info_log_entries(part_info->first_log_entry);
  mysql_mutex_unlock(&LOCK_gdl);
  part_info->first_log_entry= old_first_log_entry;
  part_info->frm_log_entry= NULL;
  my_error(ER_DDL_LOG_ERROR, MYF(0));
  DBUG_RETURN(TRUE);
}


int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt)
{
  DBUG_ENTER("ha_partition::preload_keys");

  DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS));
}


static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val,
                                     partition_info *part_info, THD *thd)
{
  bool table_engine_set;
  handlerton *engine_type= part_info->default_engine_type;
  handlerton *old_engine_type= engine_type;
  DBUG_ENTER("check_native_partitioned");

  if (create_info->used_fields & HA_CREATE_USED_ENGINE)
  {
    table_engine_set= TRUE;
    engine_type= create_info->db_type;
  }
  else
  {
    table_engine_set= FALSE;
    if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
    {
      table_engine_set= TRUE;
      DBUG_ASSERT(engine_type && engine_type != partition_hton);
    }
  }
  DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u",
                       ha_resolve_storage_engine_name(engine_type),
                       table_engine_set));
  if (part_info->check_engine_mix(engine_type, table_engine_set))
    goto error;

  /*
    All engines are of the same type. Check if this engine supports
    native partitioning.
  */

  if (!engine_type)
    engine_type= old_engine_type;
  DBUG_PRINT("info", ("engine_type = %s",
              ha_resolve_storage_engine_name(engine_type)));
  if (engine_type->partition_flags &&
      (engine_type->partition_flags() & HA_CAN_PARTITION))
  {
    create_info->db_type= engine_type;
    DBUG_PRINT("info", ("Changed to native partitioning"));
    *ret_val= TRUE;
  }
  DBUG_RETURN(FALSE);
error:
  /*
    Mixed engines not yet supported but when supported it will need
    the partition handler
  */
  my_error(ER_MIX_HANDLER_ERROR, MYF(0));
  *ret_val= FALSE;
  DBUG_RETURN(TRUE);
}


void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
{
  /*
    Fix for bug#38751, some engines needs info-calls in ALTER.
    Archive need this since it flushes in ::info.
    HA_STATUS_AUTO is optimized so it will not always be forwarded
    to all partitions, but HA_STATUS_VARIABLE will.
  */
  info(HA_STATUS_VARIABLE);

  info(HA_STATUS_AUTO);

  if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
    create_info->auto_increment_value= stats.auto_increment_value;

  create_info->data_file_name= create_info->index_file_name = NULL;
  return;
}


double ha_partition::scan_time()
{
  double scan_time= 0;
  handler **file;
  DBUG_ENTER("ha_partition::scan_time");

  for (file= m_file; *file; file++)
    if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
      scan_time+= (*file)->scan_time();
  DBUG_RETURN(scan_time);
}


int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
{
  int error= 0;
  List_iterator<partition_element> part_it(m_part_info->partitions);
  uint num_parts= m_part_info->num_parts;
  uint num_subparts= m_part_info->num_subparts;
  uint i= 0;
  DBUG_ENTER("ha_partition::truncate_partition");

  /* Only binlog when it starts any call to the partitions handlers */
  *binlog_stmt= false;

  if (set_part_state(alter_info, m_part_info, PART_ADMIN))
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

  /*
    TRUNCATE also means resetting auto_increment. Hence, reset
    it so that it will be initialized again at the next use.
  */
  lock_auto_increment();
  table_share->ha_part_data->next_auto_inc_val= 0;
  table_share->ha_part_data->auto_inc_initialized= FALSE;
  unlock_auto_increment();

  *binlog_stmt= true;

  do
  {
    partition_element *part_elem= part_it++;
    if (part_elem->part_state == PART_ADMIN)
    {
      if (m_is_sub_partitioned)
      {
        List_iterator<partition_element>
                                    subpart_it(part_elem->subpartitions);
        partition_element *sub_elem;
        uint j= 0, part;
        do
        {
          sub_elem= subpart_it++;
          part= i * num_subparts + j;
          DBUG_PRINT("info", ("truncate subpartition %u (%s)",
                              part, sub_elem->partition_name));
          if ((error= m_file[part]->ha_truncate()))
            break;
          sub_elem->part_state= PART_NORMAL;
        } while (++j < num_subparts);
      }
      else
      {
        DBUG_PRINT("info", ("truncate partition %u (%s)", i,
                            part_elem->partition_name));
        error= m_file[i]->ha_truncate();
      }
      part_elem->part_state= PART_NORMAL;
    }
  } while (!error && (++i < num_parts));
  DBUG_RETURN(error);
}


int check_signed_flag(partition_info *part_info)
{
  int error= 0;
  uint i= 0;
  if (part_info->part_type != HASH_PARTITION &&
      part_info->part_expr->unsigned_flag)
  {
    List_iterator<partition_element> part_it(part_info->partitions);
    do
    {
      partition_element *part_elem= part_it++;

      if (part_elem->signed_flag)
      {
        my_error(ER_PARTITION_CONST_DOMAIN_ERROR, MYF(0));
        error= ER_PARTITION_CONST_DOMAIN_ERROR;
        break;
      }
    } while (++i < part_info->num_parts);
  }
  return error;
}


int ha_partition::index_read_idx_map(uchar *buf, uint index,
                                     const uchar *key,
                                     key_part_map keypart_map,
                                     enum ha_rkey_function find_flag)
{
  int error= HA_ERR_KEY_NOT_FOUND;
  DBUG_ENTER("ha_partition::index_read_idx_map");

  if (find_flag == HA_READ_KEY_EXACT)
  {
    uint part;
    m_start_key.key= key;
    m_start_key.keypart_map= keypart_map;
    m_start_key.flag= find_flag;
    m_start_key.length= calculate_key_len(table, index, m_start_key.key,
                                          m_start_key.keypart_map);

    get_partition_set(table, buf, index, &m_start_key, &m_part_spec);

    /*
      We have either found exactly 1 partition
      (in which case start_part == end_part)
      or no matching partitions (start_part > end_part)
    */
    DBUG_ASSERT(m_part_spec.start_part >= m_part_spec.end_part);

    for (part= m_part_spec.start_part; part <= m_part_spec.end_part; part++)
    {
      if (bitmap_is_set(&(m_part_info->used_partitions), part))
      {
        error= m_file[part]->index_read_idx_map(buf, index, key,
                                                keypart_map, find_flag);
        if (error != HA_ERR_KEY_NOT_FOUND &&
            error != HA_ERR_END_OF_FILE)
          break;
      }
    }
    if (part <= m_part_spec.end_part)
      m_last_part= part;
  }
  else
  {
    /*
      If not only used with READ_EXACT, we should investigate if possible
      to optimize for other find_flag's as well.
    */
    DBUG_ASSERT(0);
    /* fall back on the default implementation */
    error= handler::index_read_idx_map(buf, index, key, keypart_map, find_flag);
  }
  DBUG_RETURN(error);
}


int ha_partition::create_handler_files(const char *path,
                                       const char *old_path,
                                       int action_flag,
                                       HA_CREATE_INFO *create_info)
{
  DBUG_ENTER("ha_partition::create_handler_files()");

  /*
    We need to update total number of parts since we might write the handler
    file as part of a partition management command
  */
  if (action_flag == CHF_DELETE_FLAG ||
      action_flag == CHF_RENAME_FLAG)
  {
    char name[FN_REFLEN];
    char old_name[FN_REFLEN];

    strxmov(name, path, ha_par_ext, NullS);
    strxmov(old_name, old_path, ha_par_ext, NullS);
    if ((action_flag == CHF_DELETE_FLAG &&
         mysql_file_delete(key_file_partition, name, MYF(MY_WME))) ||
        (action_flag == CHF_RENAME_FLAG &&
         mysql_file_rename(key_file_partition, old_name, name, MYF(MY_WME))))
    {
      DBUG_RETURN(TRUE);
    }
  }
  else if (action_flag == CHF_CREATE_FLAG)
  {
    if (create_handler_file(path))
    {
      my_error(ER_CANT_CREATE_HANDLER_FILE, MYF(0));
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN(0);
}


static int add_key_with_algorithm(File fptr, partition_info *part_info,
                                  const char *current_comment_start)
{
  int err= 0;
  err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);

  /*
    current_comment_start is given when called from SHOW CREATE TABLE,
    Then only add ALGORITHM = 1, not the default 2 or non-set 0!
    For .frm current_comment_start is NULL, then add ALGORITHM if != 0.
  */
  if (part_info->key_algorithm == partition_info::KEY_ALGORITHM_51 || // SHOW
      (!current_comment_start &&                                      // .frm
       (part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)))
  {
    /* If we already are within a comment, end that comment first. */
    if (current_comment_start)
      err+= add_string(fptr, "*/ ");
    err+= add_string(fptr, "/*!50531 ");
    err+= add_part_key_word(fptr, partition_keywords[PKW_ALGORITHM].str);
    err+= add_equal(fptr);
    err+= add_space(fptr);
    err+= add_int(fptr, part_info->key_algorithm);
    err+= add_space(fptr);
    err+= add_string(fptr, "*/ ");
    if (current_comment_start)
    {
      /* Skip new line. */
      if (current_comment_start[0] == '\n')
        current_comment_start++;
      err+= add_string(fptr, current_comment_start);
      err+= add_space(fptr);
    }
  }
  return err;
}


static bool create_full_part_field_array(THD *thd, TABLE *table,
                                         partition_info *part_info)
{
  bool result= FALSE;
  Field **ptr;
  my_bitmap_map *bitmap_buf;
  DBUG_ENTER("create_full_part_field_array");

  if (!part_info->is_sub_partitioned())
  {
    part_info->full_part_field_array= part_info->part_field_array;
    part_info->num_full_part_fields= part_info->num_part_fields;
  }
  else
  {
    Field *field, **field_array;
    uint num_part_fields=0, size_field_array;
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
        num_part_fields++;
    }
    size_field_array= (num_part_fields+1)*sizeof(Field*);
    field_array= (Field**)sql_calloc(size_field_array);
    if (unlikely(!field_array))
    {
      mem_alloc_error(size_field_array);
      result= TRUE;
      goto end;
    }
    num_part_fields= 0;
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
        field_array[num_part_fields++]= field;
    }
    field_array[num_part_fields]=0;
    part_info->full_part_field_array= field_array;
    part_info->num_full_part_fields= num_part_fields;
  }

  /*
    Initialize the set of all fields used in partition and subpartition
    expression. Required for testing of partition fields in write_set
    when updating. We need to set all bits in read_set because the row
    may need to be inserted in a different [sub]partition.
  */
  if (!(bitmap_buf= (my_bitmap_map*)
        thd->alloc(bitmap_buffer_size(table->s->fields))))
  {
    mem_alloc_error(bitmap_buffer_size(table->s->fields));
    result= TRUE;
    goto end;
  }
  if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
                  table->s->fields, FALSE))
  {
    mem_alloc_error(table->s->fields);
    result= TRUE;
    goto end;
  }
  /*
    full_part_field_array may be NULL if storage engine supports native
    partitioning.
  */
  if ((ptr= part_info->full_part_field_array))
    for (; *ptr; ptr++)
      bitmap_set_bit(&part_info->full_part_field_set, (*ptr)->field_index);

end:
  DBUG_RETURN(result);
}