static pj_status_t match_offer(pj_pool_t *pool,
			       pj_bool_t prefer_remote_codec_order,
                               pj_bool_t answer_with_multiple_codecs,
			       const pjmedia_sdp_media *offer,
			       const pjmedia_sdp_media *preanswer,
			       const pjmedia_sdp_session *preanswer_sdp,
			       pjmedia_sdp_media **p_answer)
{
    unsigned i;
    pj_bool_t master_has_codec = 0,
	      master_has_other = 0,
	      found_matching_codec = 0,
	      found_matching_telephone_event = 0,
	      found_matching_other = 0;
    unsigned pt_answer_count = 0;
    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
    pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT];
    pjmedia_sdp_media *answer;
    const pjmedia_sdp_media *master, *slave;
    unsigned nclockrate = 0, clockrate[PJMEDIA_MAX_SDP_FMT];
    unsigned ntel_clockrate = 0, tel_clockrate[PJMEDIA_MAX_SDP_FMT];

    /* If offer has zero port, just clone the offer */
    if (offer->desc.port == 0) {
	answer = sdp_media_clone_deactivate(pool, offer, preanswer,
					    preanswer_sdp);
	*p_answer = answer;
	return PJ_SUCCESS;
    }

    /* If the preanswer define zero port, this media is being rejected,
     * just clone the preanswer.
     */
    if (preanswer->desc.port == 0) {
	answer = pjmedia_sdp_media_clone(pool, preanswer);
	*p_answer = answer;
	return PJ_SUCCESS;
    }

    /* Set master/slave negotiator based on prefer_remote_codec_order. */
    if (prefer_remote_codec_order) {
	master = offer;
	slave  = preanswer;
    } else {
	master = preanswer;
	slave  = offer;
    }

    /* With the addition of telephone-event and dodgy MS RTC SDP,
     * the answer generation algorithm looks really shitty...
     */
    for (i=0; i<master->desc.fmt_count; ++i) {
	unsigned j;

	if (pj_isdigit(*master->desc.fmt[i].ptr)) {
	    /* This is normal/standard payload type, where it's identified
	     * by payload number.
	     */
	    unsigned pt;

	    pt = pj_strtoul(&master->desc.fmt[i]);

	    if (pt < 96) {
		/* For static payload type, it's enough to compare just
		 * the payload number.
		 */

		master_has_codec = 1;

		/* We just need to select one codec if not allowing multiple.
		 * Continue if we have selected matching codec for previous
		 * payload.
		 */
		if (!answer_with_multiple_codecs && found_matching_codec)
		    continue;

		/* Find matching codec in local descriptor. */
		for (j=0; j<slave->desc.fmt_count; ++j) {
		    unsigned p;
		    p = pj_strtoul(&slave->desc.fmt[j]);
		    if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) {
			unsigned k;

			found_matching_codec = 1;
			pt_offer[pt_answer_count] = slave->desc.fmt[j];
			pt_answer[pt_answer_count++] = slave->desc.fmt[j];

			/* Take note of clock rate for tel-event. Note: for
			 * static PT, we assume the clock rate is 8000.
			 */
			for (k=0; k<nclockrate; ++k)
			    if (clockrate[k] == 8000)
				break;
			if (k == nclockrate)
			    clockrate[nclockrate++] = 8000;
			break;
		    }
		}

	    } else {
		/* This is dynamic payload type.
		 * For dynamic payload type, we must look the rtpmap and
		 * compare the encoding name.
		 */
		const pjmedia_sdp_attr *a;
		pjmedia_sdp_rtpmap or_;
		pj_bool_t is_codec = 0;

		/* Get the rtpmap for the payload type in the master. */
		a = pjmedia_sdp_media_find_attr2(master, "rtpmap",
						 &master->desc.fmt[i]);
		if (!a) {
		    pj_assert(!"Bug! Offer should have been validated");
		    return PJMEDIA_SDP_EMISSINGRTPMAP;
		}
		pjmedia_sdp_attr_get_rtpmap(a, &or_);

		if (pj_stricmp2(&or_.enc_name, "telephone-event")) {
		    master_has_codec = 1;
		    if (!answer_with_multiple_codecs && found_matching_codec)
			continue;
		    is_codec = 1;
		}

		/* Find paylaod in our initial SDP with matching
		 * encoding name and clock rate.
		 */
		for (j=0; j<slave->desc.fmt_count; ++j) {
		    a = pjmedia_sdp_media_find_attr2(slave, "rtpmap",
						     &slave->desc.fmt[j]);
		    if (a) {
			pjmedia_sdp_rtpmap lr;
			pjmedia_sdp_attr_get_rtpmap(a, &lr);

			/* See if encoding name, clock rate, and
			 * channel count  match
			 */
			if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
			    or_.clock_rate == lr.clock_rate &&
			    (pj_stricmp(&or_.param, &lr.param)==0 ||
			     (lr.param.slen==0 && or_.param.slen==1 &&
						 *or_.param.ptr=='1') ||
			     (or_.param.slen==0 && lr.param.slen==1 &&
						  *lr.param.ptr=='1')))
			{
			    /* Match! */
			    if (is_codec) {
				pjmedia_sdp_media *o_med, *a_med;
				unsigned o_fmt_idx, a_fmt_idx;
				unsigned k;

				o_med = (pjmedia_sdp_media*)offer;
				a_med = (pjmedia_sdp_media*)preanswer;
				o_fmt_idx = prefer_remote_codec_order? i:j;
				a_fmt_idx = prefer_remote_codec_order? j:i;

				/* Call custom format matching callbacks */
				if (custom_fmt_match(pool, &or_.enc_name,
						     o_med, o_fmt_idx,
						     a_med, a_fmt_idx,
						     ALLOW_MODIFY_ANSWER) !=
				    PJ_SUCCESS)
				{
				    continue;
				}
				found_matching_codec = 1;

				/* Take note of clock rate for tel-event */
				for (k=0; k<nclockrate; ++k)
				    if (clockrate[k] == or_.clock_rate)
					break;
				if (k == nclockrate)
				    clockrate[nclockrate++] = or_.clock_rate;
			    } else {
			    	unsigned k;

				/* Keep track of tel-event clock rate,
				 * to prevent duplicate.
				 */
				for (k=0; k<ntel_clockrate; ++k)
				    if (tel_clockrate[k] == or_.clock_rate)
					break;
				if (k < ntel_clockrate)
				    continue;

				tel_clockrate[ntel_clockrate++] = or_.clock_rate;
				found_matching_telephone_event = 1;
			    }

			    pt_offer[pt_answer_count] =
						prefer_remote_codec_order?
						offer->desc.fmt[i]:
						offer->desc.fmt[j];
			    pt_answer[pt_answer_count++] =
						prefer_remote_codec_order?
						preanswer->desc.fmt[j]:
						preanswer->desc.fmt[i];
			    break;
			}
		    }
		}
	    }

	} else {
	    /* This is a non-standard, brain damaged SDP where the payload
	     * type is non-numeric. It exists e.g. in Microsoft RTC based
	     * UA, to indicate instant messaging capability.
	     * Example:
	     *	- m=x-ms-message 5060 sip null
	     */
	    master_has_other = 1;
	    if (found_matching_other)
		continue;

	    for (j=0; j<slave->desc.fmt_count; ++j) {
		if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) {
		    /* Match */
		    found_matching_other = 1;
		    pt_offer[pt_answer_count] = prefer_remote_codec_order?
						offer->desc.fmt[i]:
						offer->desc.fmt[j];
		    pt_answer[pt_answer_count++] = prefer_remote_codec_order?
						   preanswer->desc.fmt[j]:
						   preanswer->desc.fmt[i];
		    break;
		}
	    }
	}
    }

    /* See if all types of master can be matched. */
    if (master_has_codec && !found_matching_codec) {
	return PJMEDIA_SDPNEG_NOANSCODEC;
    }

    /* If this comment is removed, negotiation will fail if remote has offered
       telephone-event and local is not configured with telephone-event

    if (offer_has_telephone_event && !found_matching_telephone_event) {
	return PJMEDIA_SDPNEG_NOANSTELEVENT;
    }
    */

    if (master_has_other && !found_matching_other) {
	return PJMEDIA_SDPNEG_NOANSUNKNOWN;
    }

    /* Seems like everything is in order. */

    /* Remove unwanted telephone-event formats. */
    if (found_matching_telephone_event) {
	pj_str_t first_televent_offer = {0};
	pj_str_t first_televent_answer = {0};
	unsigned matched_cnt = 0;

	for (i=0; i<pt_answer_count; ) {
	    const pjmedia_sdp_attr *a;
	    pjmedia_sdp_rtpmap r;
	    unsigned j;

	    /* Skip static PT, as telephone-event uses dynamic PT */
	    if (!pj_isdigit(*pt_answer[i].ptr) || pj_strtol(&pt_answer[i])<96)
	    {
		++i;
		continue;
	    }

	    /* Get the rtpmap for format. */
	    a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap",
					     &pt_answer[i]);
	    pj_assert(a);
	    pjmedia_sdp_attr_get_rtpmap(a, &r);

	    /* Only care for telephone-event format */
	    if (pj_stricmp2(&r.enc_name, "telephone-event")) {
		++i;
		continue;
	    }

	    if (first_televent_offer.slen == 0) {
		first_televent_offer = pt_offer[i];
		first_televent_answer = pt_answer[i];
	    }

	    for (j=0; j<nclockrate; ++j) {
		if (r.clock_rate==clockrate[j])
		    break;
	    }

	    /* This tel-event's clockrate is unwanted, remove the tel-event */
	    if (j==nclockrate) {
		pj_array_erase(pt_answer, sizeof(pt_answer[0]),
			       pt_answer_count, i);
		pj_array_erase(pt_offer, sizeof(pt_offer[0]),
			       pt_answer_count, i);
		pt_answer_count--;
	    } else {
		++matched_cnt;
		++i;
	    }
	}

	/* Tel-event is wanted, but no matched clock rate (to the selected
	 * audio codec), just put back any first matched tel-event formats.
	 */
	if (!matched_cnt) {
	    pt_offer[pt_answer_count] = first_televent_offer;
	    pt_answer[pt_answer_count++] = first_televent_answer;
	}
    }

    /* Build the answer by cloning from preanswer, and reorder the payload
     * to suit the offer.
     */
    answer = pjmedia_sdp_media_clone(pool, preanswer);
    for (i=0; i<pt_answer_count; ++i) {
	unsigned j;
	for (j=i; j<answer->desc.fmt_count; ++j) {
	    if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
		break;
	}
	pj_assert(j != answer->desc.fmt_count);
	str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
    }

    /* Remove unwanted local formats. */
    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
	pjmedia_sdp_attr *a;

	/* Remove rtpmap for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "rtpmap",
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}

	/* Remove fmtp for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "fmtp",
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}
    }
    answer->desc.fmt_count = pt_answer_count;

#if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT
    apply_answer_symmetric_pt(pool, answer, pt_answer_count,
			      pt_offer, pt_answer);
#endif

    /* Update media direction. */
    update_media_direction(pool, offer, answer);

    *p_answer = answer;
    return PJ_SUCCESS;
}


static pj_status_t process_answer(pj_pool_t *pool,
				  pjmedia_sdp_session *local_offer,
				  pjmedia_sdp_session *answer,
				  pj_bool_t allow_asym,
				  pjmedia_sdp_session **p_active)
{
    unsigned omi = 0; /* Offer media index */
    unsigned ami = 0; /* Answer media index */
    pj_bool_t has_active = PJ_FALSE;
    pjmedia_sdp_session *offer;
    pj_status_t status;

    /* Check arguments. */
    PJ_ASSERT_RETURN(pool && local_offer && answer && p_active, PJ_EINVAL);

    /* Duplicate local offer SDP. */
    offer = pjmedia_sdp_session_clone(pool, local_offer);

    /* Check that media count match between offer and answer */
    // Ticket #527, different media count is allowed for more interoperability,
    // however, the media order must be same between offer and answer.
    // if (offer->media_count != answer->media_count)
    //	   return PJMEDIA_SDPNEG_EMISMEDIA;

    /* Now update each media line in the offer with the answer. */
    for (; omi<offer->media_count; ++omi) {
	if (ami == answer->media_count) {
	    /* The answer has less media than the offer */
	    pjmedia_sdp_media *am;

	    /* Generate matching-but-disabled-media for the answer */
	    am = sdp_media_clone_deactivate(pool, offer->media[omi],
	                                    offer->media[omi], offer);
	    answer->media[answer->media_count++] = am;
	    ++ami;

	    /* Deactivate our media offer too */
	    pjmedia_sdp_media_deactivate(pool, offer->media[omi]);

	    /* No answer media to be negotiated */
	    continue;
	}

	status = process_m_answer(pool, offer->media[omi], answer->media[ami],
				  allow_asym);

	/* If media type is mismatched, just disable the media. */
	if (status == PJMEDIA_SDPNEG_EINVANSMEDIA) {
	    pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
	    continue;
	}
	/* No common format in the answer media. */
	else if (status == PJMEDIA_SDPNEG_EANSNOMEDIA) {
	    pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
	    pjmedia_sdp_media_deactivate(pool, answer->media[ami]);
	}
	/* Return the error code, for other errors. */
	else if (status != PJ_SUCCESS) {
	    return status;
	}

	if (offer->media[omi]->desc.port != 0)
	    has_active = PJ_TRUE;

	++ami;
    }

    *p_active = offer;

    return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA;
}

Merge pull request from GHSA-hvq6-f89p-frvp


static pj_status_t custom_fmt_match(pj_pool_t *pool,
				    const pj_str_t *fmt_name,
				    pjmedia_sdp_media *offer,
				    unsigned o_fmt_idx,
				    pjmedia_sdp_media *answer,
				    unsigned a_fmt_idx,
				    unsigned option)
{
    unsigned i;

    for (i = 0; i < fmt_match_cb_cnt; ++i) {
	if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) {
	    pj_assert(fmt_match_cb[i].cb);
	    return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx,
					 answer, a_fmt_idx,
					 option);
	}
    }

    /* Not customized format matching found, should be matched */
    return PJ_SUCCESS;
}


PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_answer_multiple_codecs(
                        pjmedia_sdp_neg *neg,
                        pj_bool_t answer_multiple)
{
    PJ_ASSERT_RETURN(neg, PJ_EINVAL);
    neg->answer_with_multiple_codecs = answer_multiple;
    return PJ_SUCCESS;
}


PJ_DEF(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg)
{
    PJ_ASSERT_RETURN(neg, PJ_FALSE);

    return neg->answer_was_remote;
}


PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state)
{
    if ((int)state >=0 && state < (pjmedia_sdp_neg_state)PJ_ARRAY_SIZE(state_str))
	return state_str[state];

    return "<?UNKNOWN?>";
}


static void remove_all_media_directions(pjmedia_sdp_media *m)
{
    pjmedia_sdp_media_remove_all_attr(m, "inactive");
    pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
    pjmedia_sdp_media_remove_all_attr(m, "sendonly");
    pjmedia_sdp_media_remove_all_attr(m, "recvonly");
}


PJ_DEF(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg)
{
    PJ_ASSERT_RETURN(neg, PJ_EINVAL);

    /* Must be in LOCAL_OFFER state. */
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
		     neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER,
		     PJMEDIA_SDPNEG_EINSTATE);

    if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER &&
	neg->active_local_sdp)
    {
	/* Increment next version number. This happens if for example
	 * the reinvite offer is rejected by 488. If we don't increment
	 * the version here, the next offer will have the same version.
	 */
	neg->active_local_sdp->origin.version++;
    }

    /* Revert back initial SDP */
    if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
	neg->initial_sdp = neg->initial_sdp_tmp;

    /* Clear temporary SDP */
    neg->initial_sdp_tmp = NULL;
    neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
    neg->has_remote_answer = PJ_FALSE;

    /* Reset state to done */
    neg->state = PJMEDIA_SDP_NEG_STATE_DONE;

    return PJ_SUCCESS;
}


static pj_status_t create_answer( pj_pool_t *pool,
				  pj_bool_t prefer_remote_codec_order,
                                  pj_bool_t answer_with_multiple_codecs,
				  const pjmedia_sdp_session *initial,
				  const pjmedia_sdp_session *offer,
				  pjmedia_sdp_session **p_answer)
{
    pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA;
    pj_bool_t has_active = PJ_FALSE;
    pjmedia_sdp_session *answer;
    char media_used[PJMEDIA_MAX_SDP_MEDIA];
    unsigned i;

    /* Validate remote offer.
     * This should have been validated before.
     */
    PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status);

    /* Create initial answer by duplicating initial SDP,
     * but clear all media lines. The media lines will be filled up later.
     */
    answer = pjmedia_sdp_session_clone(pool, initial);
    PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM);

    answer->media_count = 0;

    pj_bzero(media_used, sizeof(media_used));

    /* For each media line, create our answer based on our initial
     * capability.
     */
    for (i=0; i<offer->media_count; ++i) {
	const pjmedia_sdp_media *om;	/* offer */
	const pjmedia_sdp_media *im;	/* initial media */
	pjmedia_sdp_media *am = NULL;	/* answer/result */
	unsigned j;

	om = offer->media[i];

	/* Find media description in our initial capability that matches
	 * the media type and transport type of offer's media, has
	 * matching codec, and has not been used to answer other offer.
	 */
	for (im=NULL, j=0; j<initial->media_count; ++j) {
	    im = initial->media[j];
	    if (pj_strcmp(&om->desc.media, &im->desc.media)==0 &&
		pj_strcmp(&om->desc.transport, &im->desc.transport)==0 &&
		media_used[j] == 0)
	    {
                pj_status_t status2;

		/* See if it has matching codec. */
		status2 = match_offer(pool, prefer_remote_codec_order,
                                      answer_with_multiple_codecs,
				      om, im, initial, &am);
		if (status2 == PJ_SUCCESS) {
		    /* Mark media as used. */
		    media_used[j] = 1;
		    break;
                } else {
                    status = status2;
                }
	    }
	}

	if (j==initial->media_count) {
	    /* No matching media.
	     * Reject the offer by setting the port to zero in the answer.
	     */
	    /* For simplicity in the construction of the answer, we'll
	     * just clone the media from the offer. Anyway receiver will
	     * ignore anything in the media once it sees that the port
	     * number is zero.
	     */
	    am = sdp_media_clone_deactivate(pool, om, om, answer);
	} else {
	    /* The answer is in am */
	    pj_assert(am != NULL);
	}

	/* Add the media answer */
	answer->media[answer->media_count++] = am;

	/* Check if this media is active.*/
	if (am->desc.port != 0)
	    has_active = PJ_TRUE;
    }

    *p_answer = answer;

    return has_active ? PJ_SUCCESS : status;
}


static int __init lp_init_module (void)
{
	if (parport[0]) {
		/* The user gave some parameters.  Let's see what they were.  */
		if (!strncmp(parport[0], "auto", 4))
			parport_nr[0] = LP_PARPORT_AUTO;
		else {
			int n;
			for (n = 0; n < LP_NO && parport[n]; n++) {
				if (!strncmp(parport[n], "none", 4))
					parport_nr[n] = LP_PARPORT_NONE;
				else {
					char *ep;
					unsigned long r = simple_strtoul(parport[n], &ep, 0);
					if (ep != parport[n])
						parport_nr[n] = r;
					else {
						printk(KERN_ERR "lp: bad port specifier `%s'\n", parport[n]);
						return -ENODEV;
					}
				}
			}
		}
	}

	return lp_init();
}

char: lp: fix possible integer overflow in lp_setup()

The lp_setup() code doesn't apply any bounds checking when passing
"lp=none", and only in this case, resulting in an overflow of the
parport_nr[] array. All versions in Git history are affected.

Reported-By: Roee Hay <roee.hay@hcl.com>
Cc: Ben Hutchings <ben@decadent.org.uk>
Cc: stable@vger.kernel.org
Signed-off-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

#define LP_NO 8

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static void lp_release_parport(struct lp_struct *this_lp)
{
	if (test_and_clear_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
		parport_release (this_lp->dev);
	}
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define LP_PARPORT_CLAIMED 2

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int lp_reset(int minor)
{
	int retval;
	lp_claim_parport_or_block (&lp_table[minor]);
	w_ctr(minor, LP_PSELECP);
	udelay (LP_DELAY);
	w_ctr(minor, LP_PSELECP | LP_PINITP);
	retval = r_str(minor);
	lp_release_parport (&lp_table[minor]);
	return retval;
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int lp_check_status(int minor)
{
	int error = 0;
	unsigned int last = lp_table[minor].last_error;
	unsigned char status = r_str(minor);
	if ((status & LP_PERRORP) && !(LP_F(minor) & LP_CAREFUL))
		/* No error. */
		last = 0;
	else if ((status & LP_POUTPA)) {
		if (last != LP_POUTPA) {
			last = LP_POUTPA;
			printk(KERN_INFO "lp%d out of paper\n", minor);
		}
		error = -ENOSPC;
	} else if (!(status & LP_PSELECD)) {
		if (last != LP_PSELECD) {
			last = LP_PSELECD;
			printk(KERN_INFO "lp%d off-line\n", minor);
		}
		error = -EIO;
	} else if (!(status & LP_PERRORP)) {
		if (last != LP_PERRORP) {
			last = LP_PERRORP;
			printk(KERN_INFO "lp%d on fire\n", minor);
		}
		error = -EIO;
	} else {
		last = 0; /* Come here if LP_CAREFUL is set and no
                             errors are reported. */
	}

	lp_table[minor].last_error = last;

	if (last != 0)
		lp_error(minor);

	return error;
}


static int lp_open(struct inode * inode, struct file * file)
{
	unsigned int minor = iminor(inode);
	int ret = 0;

	mutex_lock(&lp_mutex);
	if (minor >= LP_NO) {
		ret = -ENXIO;
		goto out;
	}
	if ((LP_F(minor) & LP_EXIST) == 0) {
		ret = -ENXIO;
		goto out;
	}
	if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor))) {
		ret = -EBUSY;
		goto out;
	}
	/* If ABORTOPEN is set and the printer is offline or out of paper,
	   we may still want to open it to perform ioctl()s.  Therefore we
	   have commandeered O_NONBLOCK, even though it is being used in
	   a non-standard manner.  This is strictly a Linux hack, and
	   should most likely only ever be used by the tunelp application. */
	if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
		int status;
		lp_claim_parport_or_block (&lp_table[minor]);
		status = r_str(minor);
		lp_release_parport (&lp_table[minor]);
		if (status & LP_POUTPA) {
			printk(KERN_INFO "lp%d out of paper\n", minor);
			LP_F(minor) &= ~LP_BUSY;
			ret = -ENOSPC;
			goto out;
		} else if (!(status & LP_PSELECD)) {
			printk(KERN_INFO "lp%d off-line\n", minor);
			LP_F(minor) &= ~LP_BUSY;
			ret = -EIO;
			goto out;
		} else if (!(status & LP_PERRORP)) {
			printk(KERN_ERR "lp%d printer error\n", minor);
			LP_F(minor) &= ~LP_BUSY;
			ret = -EIO;
			goto out;
		}
	}
	lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
	if (!lp_table[minor].lp_buffer) {
		LP_F(minor) &= ~LP_BUSY;
		ret = -ENOMEM;
		goto out;
	}
	/* Determine if the peripheral supports ECP mode */
	lp_claim_parport_or_block (&lp_table[minor]);
	if ( (lp_table[minor].dev->port->modes & PARPORT_MODE_ECP) &&
             !parport_negotiate (lp_table[minor].dev->port,
                                 IEEE1284_MODE_ECP)) {
		printk (KERN_INFO "lp%d: ECP mode\n", minor);
		lp_table[minor].best_mode = IEEE1284_MODE_ECP;
	} else {
		lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
	}
	/* Leave peripheral in compatibility mode */
	parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
	lp_release_parport (&lp_table[minor]);
	lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
out:
	mutex_unlock(&lp_mutex);
	return ret;
}


static long lp_ioctl(struct file *file, unsigned int cmd,
			unsigned long arg)
{
	unsigned int minor;
	struct timeval par_timeout;
	int ret;

	minor = iminor(file_inode(file));
	mutex_lock(&lp_mutex);
	switch (cmd) {
	case LPSETTIMEOUT:
		if (copy_from_user(&par_timeout, (void __user *)arg,
					sizeof (struct timeval))) {
			ret = -EFAULT;
			break;
		}
		ret = lp_set_timeout(minor, &par_timeout);
		break;
	default:
		ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg);
		break;
	}
	mutex_unlock(&lp_mutex);

	return ret;
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int lp_register(int nr, struct parport *port)
{
	lp_table[nr].dev = parport_register_device(port, "lp",
						   lp_preempt, NULL, NULL, 0,
						   (void *) &lp_table[nr]);
	if (lp_table[nr].dev == NULL)
		return 1;
	lp_table[nr].flags |= LP_EXIST;

	if (reset)
		lp_reset(nr);

	device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
		      "lp%d", nr);

	printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
	       (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");

#ifdef CONFIG_LP_CONSOLE
	if (!nr) {
		if (port->modes & PARPORT_MODE_SAFEININT) {
			register_console(&lpcons);
			console_registered = port;
			printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP);
		} else
			printk (KERN_ERR "lp%d: cannot run console on %s\n",
				CONSOLE_LP, port->name);
	}
#endif

	return 0;
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

#define CONSOLE_LP 0

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int __init lp_setup (char *str)
{
	static int parport_ptr;
	int x;

	if (get_option(&str, &x)) {
		if (x == 0) {
			/* disable driver on "lp=" or "lp=0" */
			parport_nr[0] = LP_PARPORT_OFF;
		} else {
			printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", x);
			return 0;
		}
	} else if (!strncmp(str, "parport", 7)) {
		int n = simple_strtoul(str+7, NULL, 10);
		if (parport_ptr < LP_NO)
			parport_nr[parport_ptr++] = n;
		else
			printk(KERN_INFO "lp: too many ports, %s ignored.\n",
			       str);
	} else if (!strcmp(str, "auto")) {
		parport_nr[0] = LP_PARPORT_AUTO;
	} else if (!strcmp(str, "none")) {
		if (parport_ptr < LP_NO)
			parport_nr[parport_ptr++] = LP_PARPORT_NONE;
		else
			printk(KERN_INFO "lp: too many ports, %s ignored.\n",
			       str);
	} else if (!strcmp(str, "reset")) {
		reset = 1;
	}
	return 1;
}

#define LP_NO 8

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static void lp_console_write (struct console *co, const char *s,
			      unsigned count)
{
	struct pardevice *dev = lp_table[CONSOLE_LP].dev;
	struct parport *port = dev->port;
	ssize_t written;

	if (parport_claim (dev))
		/* Nothing we can do. */
		return;

	parport_set_timeout (dev, 0);

	/* Go to compatibility mode. */
	parport_negotiate (port, IEEE1284_MODE_COMPAT);

	do {
		/* Write the data, converting LF->CRLF as we go. */
		ssize_t canwrite = count;
		char *lf = memchr (s, '\n', count);
		if (lf)
			canwrite = lf - s;

		if (canwrite > 0) {
			written = parport_write (port, s, canwrite);

			if (written <= 0)
				continue;

			s += written;
			count -= written;
			canwrite -= written;
		}

		if (lf && canwrite <= 0) {
			const char *crlf = "\r\n";
			int i = 2;

			/* Dodge the original '\n', and put '\r\n' instead. */
			s++;
			count--;
			do {
				written = parport_write (port, crlf, i);
				if (written > 0)
					i -= written, crlf += written;
			} while (i > 0 && (CONSOLE_LP_STRICT || written > 0));
		}
	} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));

	parport_release (dev);
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

#define CONSOLE_LP 0

#define CONSOLE_LP_STRICT 1

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int lp_preempt(void *handle)
{
	struct lp_struct *this_lp = (struct lp_struct *)handle;
	set_bit(LP_PREEMPT_REQUEST, &this_lp->bits);
	return (1);
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define LP_PREEMPT_REQUEST 1

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static void lp_attach (struct parport *port)
{
	unsigned int i;

	switch (parport_nr[0]) {
	case LP_PARPORT_UNSPEC:
	case LP_PARPORT_AUTO:
		if (parport_nr[0] == LP_PARPORT_AUTO &&
		    port->probe_info[0].class != PARPORT_CLASS_PRINTER)
			return;
		if (lp_count == LP_NO) {
			printk(KERN_INFO "lp: ignoring parallel port (max. %d)\n",LP_NO);
			return;
		}
		if (!lp_register(lp_count, port))
			lp_count++;
		break;

	default:
		for (i = 0; i < LP_NO; i++) {
			if (port->number == parport_nr[i]) {
				if (!lp_register(i, port))
					lp_count++;
				break;
			}
		}
		break;
	}
}


static int lp_negotiate(struct parport * port, int mode)
{
	if (parport_negotiate (port, mode) != 0) {
		mode = IEEE1284_MODE_COMPAT;
		parport_negotiate (port, mode);
	}

	return (mode);
}


static void lp_claim_parport_or_block(struct lp_struct *this_lp)
{
	if (!test_and_set_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
		parport_claim_or_block (this_lp->dev);
	}
}


static ssize_t lp_write(struct file * file, const char __user * buf,
		        size_t count, loff_t *ppos)
{
	unsigned int minor = iminor(file_inode(file));
	struct parport *port = lp_table[minor].dev->port;
	char *kbuf = lp_table[minor].lp_buffer;
	ssize_t retv = 0;
	ssize_t written;
	size_t copy_size = count;
	int nonblock = ((file->f_flags & O_NONBLOCK) ||
			(LP_F(minor) & LP_ABORT));

#ifdef LP_STATS
	if (time_after(jiffies, lp_table[minor].lastcall + LP_TIME(minor)))
		lp_table[minor].runchars = 0;

	lp_table[minor].lastcall = jiffies;
#endif

	/* Need to copy the data from user-space. */
	if (copy_size > LP_BUFFER_SIZE)
		copy_size = LP_BUFFER_SIZE;

	if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
		return -EINTR;

	if (copy_from_user (kbuf, buf, copy_size)) {
		retv = -EFAULT;
		goto out_unlock;
	}

 	/* Claim Parport or sleep until it becomes available
 	 */
	lp_claim_parport_or_block (&lp_table[minor]);
	/* Go to the proper mode. */
	lp_table[minor].current_mode = lp_negotiate (port,
						     lp_table[minor].best_mode);

	parport_set_timeout (lp_table[minor].dev,
			     (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
			      : lp_table[minor].timeout));

	if ((retv = lp_wait_ready (minor, nonblock)) == 0)
	do {
		/* Write the data. */
		written = parport_write (port, kbuf, copy_size);
		if (written > 0) {
			copy_size -= written;
			count -= written;
			buf  += written;
			retv += written;
		}

		if (signal_pending (current)) {
			if (retv == 0)
				retv = -EINTR;

			break;
		}

		if (copy_size > 0) {
			/* incomplete write -> check error ! */
			int error;

			parport_negotiate (lp_table[minor].dev->port,
					   IEEE1284_MODE_COMPAT);
			lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;

			error = lp_wait_ready (minor, nonblock);

			if (error) {
				if (retv == 0)
					retv = error;
				break;
			} else if (nonblock) {
				if (retv == 0)
					retv = -EAGAIN;
				break;
			}

			parport_yield_blocking (lp_table[minor].dev);
			lp_table[minor].current_mode
			  = lp_negotiate (port,
					  lp_table[minor].best_mode);

		} else if (need_resched())
			schedule ();

		if (count) {
			copy_size = count;
			if (copy_size > LP_BUFFER_SIZE)
				copy_size = LP_BUFFER_SIZE;

			if (copy_from_user(kbuf, buf, copy_size)) {
				if (retv == 0)
					retv = -EFAULT;
				break;
			}
		}
	} while (count > 0);

	if (test_and_clear_bit(LP_PREEMPT_REQUEST,
			       &lp_table[minor].bits)) {
		printk(KERN_INFO "lp%d releasing parport\n", minor);
		parport_negotiate (lp_table[minor].dev->port,
				   IEEE1284_MODE_COMPAT);
		lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
		lp_release_parport (&lp_table[minor]);
	}
out_unlock:
	mutex_unlock(&lp_table[minor].port_mutex);

 	return retv;
}

static DEFINE_MUTEX(lp_mutex);

static struct lp_struct lp_table[LP_NO];

static unsigned int lp_count = 0;

static struct class *lp_class;

static struct parport *console_registered;

#define LP_PREEMPT_REQUEST 1

#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))

#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))

#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)

#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)

static const struct file_operations lp_fops = {
	.owner		= THIS_MODULE,
	.write		= lp_write,
	.unlocked_ioctl	= lp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= lp_compat_ioctl,
#endif
	.open		= lp_open,
	.release	= lp_release,
#ifdef CONFIG_PARPORT_1284
	.read		= lp_read,
#endif
	.llseek		= noop_llseek,
};

static struct console lpcons = {
	.name		= "lp",
	.write		= lp_console_write,
	.flags		= CON_PRINTBUFFER,
};

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };

static char *parport[LP_NO];

static bool reset;

static struct parport_driver lp_driver = {
	.name = "lp",
	.attach = lp_attach,
	.detach = lp_detach,
};


static int lp_release(struct inode * inode, struct file * file)
{
	unsigned int minor = iminor(inode);

	lp_claim_parport_or_block (&lp_table[minor]);
	parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
	lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
	lp_release_parport (&lp_table[minor]);
	kfree(lp_table[minor].lp_buffer);
	lp_table[minor].lp_buffer = NULL;
	LP_F(minor) &= ~LP_BUSY;
	return 0;
}


static int lp_wait_ready(int minor, int nonblock)
{
	int error = 0;

	/* If we're not in compatibility mode, we're ready now! */
	if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) {
	  return (0);
	}

	do {
		error = lp_check_status (minor);
		if (error && (nonblock || (LP_F(minor) & LP_ABORT)))
			break;
		if (signal_pending (current)) {
			error = -EINTR;
			break;
		}
	} while (error);
	return error;
}


static int __init lp_init (void)
{
	int i, err = 0;

	if (parport_nr[0] == LP_PARPORT_OFF)
		return 0;

	for (i = 0; i < LP_NO; i++) {
		lp_table[i].dev = NULL;
		lp_table[i].flags = 0;
		lp_table[i].chars = LP_INIT_CHAR;
		lp_table[i].time = LP_INIT_TIME;
		lp_table[i].wait = LP_INIT_WAIT;
		lp_table[i].lp_buffer = NULL;
#ifdef LP_STATS
		lp_table[i].lastcall = 0;
		lp_table[i].runchars = 0;
		memset (&lp_table[i].stats, 0, sizeof (struct lp_stats));
#endif
		lp_table[i].last_error = 0;
		init_waitqueue_head (&lp_table[i].waitq);
		init_waitqueue_head (&lp_table[i].dataq);
		mutex_init(&lp_table[i].port_mutex);
		lp_table[i].timeout = 10 * HZ;
	}

	if (register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
		printk (KERN_ERR "lp: unable to get major %d\n", LP_MAJOR);
		return -EIO;
	}

	lp_class = class_create(THIS_MODULE, "printer");
	if (IS_ERR(lp_class)) {
		err = PTR_ERR(lp_class);
		goto out_reg;
	}

	if (parport_register_driver (&lp_driver)) {
		printk (KERN_ERR "lp: unable to register with parport\n");
		err = -EIO;
		goto out_class;
	}

	if (!lp_count) {
		printk (KERN_INFO "lp: driver loaded but no devices found\n");
#ifndef CONFIG_PARPORT_1284
		if (parport_nr[0] == LP_PARPORT_AUTO)
			printk (KERN_INFO "lp: (is IEEE 1284 support enabled?)\n");
#endif
	}

	return 0;

out_class:
	class_destroy(lp_class);
out_reg:
	unregister_chrdev(LP_MAJOR, "lp");
	return err;
}


static void lp_cleanup_module (void)
{
	unsigned int offset;

	parport_unregister_driver (&lp_driver);

#ifdef CONFIG_LP_CONSOLE
	unregister_console (&lpcons);
#endif

	unregister_chrdev(LP_MAJOR, "lp");
	for (offset = 0; offset < LP_NO; offset++) {
		if (lp_table[offset].dev == NULL)
			continue;
		parport_unregister_device(lp_table[offset].dev);
		device_destroy(lp_class, MKDEV(LP_MAJOR, offset));
	}
	class_destroy(lp_class);
}


static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
	unsigned long arg, void __user *argp)
{
	int status;
	int retval = 0;

#ifdef LP_DEBUG
	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
#endif
	if (minor >= LP_NO)
		return -ENODEV;
	if ((LP_F(minor) & LP_EXIST) == 0)
		return -ENODEV;
	switch ( cmd ) {
		case LPTIME:
			if (arg > UINT_MAX / HZ)
				return -EINVAL;
			LP_TIME(minor) = arg * HZ/100;
			break;
		case LPCHAR:
			LP_CHAR(minor) = arg;
			break;
		case LPABORT:
			if (arg)
				LP_F(minor) |= LP_ABORT;
			else
				LP_F(minor) &= ~LP_ABORT;
			break;
		case LPABORTOPEN:
			if (arg)
				LP_F(minor) |= LP_ABORTOPEN;
			else
				LP_F(minor) &= ~LP_ABORTOPEN;
			break;
		case LPCAREFUL:
			if (arg)
				LP_F(minor) |= LP_CAREFUL;
			else
				LP_F(minor) &= ~LP_CAREFUL;
			break;
		case LPWAIT:
			LP_WAIT(minor) = arg;
			break;
		case LPSETIRQ:
			return -EINVAL;
			break;
		case LPGETIRQ:
			if (copy_to_user(argp, &LP_IRQ(minor),
					sizeof(int)))
				return -EFAULT;
			break;
		case LPGETSTATUS:
			if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
				return -EINTR;
			lp_claim_parport_or_block (&lp_table[minor]);
			status = r_str(minor);
			lp_release_parport (&lp_table[minor]);
			mutex_unlock(&lp_table[minor].port_mutex);

			if (copy_to_user(argp, &status, sizeof(int)))
				return -EFAULT;
			break;
		case LPRESET:
			lp_reset(minor);
			break;
#ifdef LP_STATS
		case LPGETSTATS:
			if (copy_to_user(argp, &LP_STAT(minor),
					sizeof(struct lp_stats)))
				return -EFAULT;
			if (capable(CAP_SYS_ADMIN))
				memset(&LP_STAT(minor), 0,
						sizeof(struct lp_stats));
			break;
#endif
 		case LPGETFLAGS:
 			status = LP_F(minor);
			if (copy_to_user(argp, &status, sizeof(int)))
				return -EFAULT;
			break;

		default:
			retval = -EINVAL;
	}
	return retval;
}


static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout)
{
	long to_jiffies;

	/* Convert to jiffies, place in lp_table */
	if ((par_timeout->tv_sec < 0) ||
	    (par_timeout->tv_usec < 0)) {
		return -EINVAL;
	}
	to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ);
	to_jiffies += par_timeout->tv_sec * (long) HZ;
	if (to_jiffies <= 0) {
		return -EINVAL;
	}
	lp_table[minor].timeout = to_jiffies;
	return 0;
}


static long lp_compat_ioctl(struct file *file, unsigned int cmd,
			unsigned long arg)
{
	unsigned int minor;
	struct timeval par_timeout;
	int ret;

	minor = iminor(file_inode(file));
	mutex_lock(&lp_mutex);
	switch (cmd) {
	case LPSETTIMEOUT:
		if (compat_get_timeval(&par_timeout, compat_ptr(arg))) {
			ret = -EFAULT;
			break;
		}
		ret = lp_set_timeout(minor, &par_timeout);
		break;
#ifdef LP_STATS
	case LPGETSTATS:
		/* FIXME: add an implementation if you set LP_STATS */
		ret = -EINVAL;
		break;
#endif
	default:
		ret = lp_do_ioctl(minor, cmd, arg, compat_ptr(arg));
		break;
	}
	mutex_unlock(&lp_mutex);

	return ret;
}


static void lp_detach (struct parport *port)
{
	/* Write this some day. */
#ifdef CONFIG_LP_CONSOLE
	if (console_registered == port) {
		unregister_console(&lpcons);
		console_registered = NULL;
	}
#endif /* CONFIG_LP_CONSOLE */
}


static void lp_error (int minor)
{
	DEFINE_WAIT(wait);
	int polling;

	if (LP_F(minor) & LP_ABORT)
		return;

	polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
	if (polling) lp_release_parport (&lp_table[minor]);
	prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
	schedule_timeout(LP_TIMEOUT_POLLED);
	finish_wait(&lp_table[minor].waitq, &wait);
	if (polling) lp_claim_parport_or_block (&lp_table[minor]);
	else parport_yield_blocking (lp_table[minor].dev);
}


static ssize_t lp_read(struct file * file, char __user * buf,
		       size_t count, loff_t *ppos)
{
	DEFINE_WAIT(wait);
	unsigned int minor=iminor(file_inode(file));
	struct parport *port = lp_table[minor].dev->port;
	ssize_t retval = 0;
	char *kbuf = lp_table[minor].lp_buffer;
	int nonblock = ((file->f_flags & O_NONBLOCK) ||
			(LP_F(minor) & LP_ABORT));

	if (count > LP_BUFFER_SIZE)
		count = LP_BUFFER_SIZE;

	if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
		return -EINTR;

	lp_claim_parport_or_block (&lp_table[minor]);

	parport_set_timeout (lp_table[minor].dev,
			     (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
			      : lp_table[minor].timeout));

	parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
	if (parport_negotiate (lp_table[minor].dev->port,
			       IEEE1284_MODE_NIBBLE)) {
		retval = -EIO;
		goto out;
	}

	while (retval == 0) {
		retval = parport_read (port, kbuf, count);

		if (retval > 0)
			break;

		if (nonblock) {
			retval = -EAGAIN;
			break;
		}

		/* Wait for data. */

		if (lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE) {
			parport_negotiate (lp_table[minor].dev->port,
					   IEEE1284_MODE_COMPAT);
			lp_error (minor);
			if (parport_negotiate (lp_table[minor].dev->port,
					       IEEE1284_MODE_NIBBLE)) {
				retval = -EIO;
				goto out;
			}
		} else {
			prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
			schedule_timeout(LP_TIMEOUT_POLLED);
			finish_wait(&lp_table[minor].waitq, &wait);
		}

		if (signal_pending (current)) {
			retval = -ERESTARTSYS;
			break;
		}

		cond_resched ();
	}
	parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
 out:
	lp_release_parport (&lp_table[minor]);

	if (retval > 0 && copy_to_user (buf, kbuf, retval))
		retval = -EFAULT;

	mutex_unlock(&lp_table[minor].port_mutex);

	return retval;
}


static struct buffer_head *__ext4_find_entry(struct inode *dir,
					     struct ext4_filename *fname,
					     struct ext4_dir_entry_2 **res_dir,
					     int *inlined)
{
	struct super_block *sb;
	struct buffer_head *bh_use[NAMEI_RA_SIZE];
	struct buffer_head *bh, *ret = NULL;
	ext4_lblk_t start, block;
	const u8 *name = fname->usr_fname->name;
	size_t ra_max = 0;	/* Number of bh's in the readahead
				   buffer, bh_use[] */
	size_t ra_ptr = 0;	/* Current index into readahead
				   buffer */
	ext4_lblk_t  nblocks;
	int i, namelen, retval;

	*res_dir = NULL;
	sb = dir->i_sb;
	namelen = fname->usr_fname->len;
	if (namelen > EXT4_NAME_LEN)
		return NULL;

	if (ext4_has_inline_data(dir)) {
		int has_inline_data = 1;
		ret = ext4_find_inline_entry(dir, fname, res_dir,
					     &has_inline_data);
		if (has_inline_data) {
			if (inlined)
				*inlined = 1;
			goto cleanup_and_exit;
		}
	}

	if ((namelen <= 2) && (name[0] == '.') &&
	    (name[1] == '.' || name[1] == '\0')) {
		/*
		 * "." or ".." will only be in the first block
		 * NFS may look up ".."; "." should be handled by the VFS
		 */
		block = start = 0;
		nblocks = 1;
		goto restart;
	}
	if (is_dx(dir)) {
		ret = ext4_dx_find_entry(dir, fname, res_dir);
		/*
		 * On success, or if the error was file not found,
		 * return.  Otherwise, fall back to doing a search the
		 * old fashioned way.
		 */
		if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR)
			goto cleanup_and_exit;
		dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
			       "falling back\n"));
		ret = NULL;
	}
	nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb);
	if (!nblocks) {
		ret = NULL;
		goto cleanup_and_exit;
	}
	start = EXT4_I(dir)->i_dir_start_lookup;
	if (start >= nblocks)
		start = 0;
	block = start;
restart:
	do {
		/*
		 * We deal with the read-ahead logic here.
		 */
		cond_resched();
		if (ra_ptr >= ra_max) {
			/* Refill the readahead buffer */
			ra_ptr = 0;
			if (block < start)
				ra_max = start - block;
			else
				ra_max = nblocks - block;
			ra_max = min(ra_max, ARRAY_SIZE(bh_use));
			retval = ext4_bread_batch(dir, block, ra_max,
						  false /* wait */, bh_use);
			if (retval) {
				ret = ERR_PTR(retval);
				ra_max = 0;
				goto cleanup_and_exit;
			}
		}
		if ((bh = bh_use[ra_ptr++]) == NULL)
			goto next;
		wait_on_buffer(bh);
		if (!buffer_uptodate(bh)) {
			EXT4_ERROR_INODE_ERR(dir, EIO,
					     "reading directory lblock %lu",
					     (unsigned long) block);
			brelse(bh);
			ret = ERR_PTR(-EIO);
			goto cleanup_and_exit;
		}
		if (!buffer_verified(bh) &&
		    !is_dx_internal_node(dir, block,
					 (struct ext4_dir_entry *)bh->b_data) &&
		    !ext4_dirblock_csum_verify(dir, bh)) {
			EXT4_ERROR_INODE_ERR(dir, EFSBADCRC,
					     "checksumming directory "
					     "block %lu", (unsigned long)block);
			brelse(bh);
			ret = ERR_PTR(-EFSBADCRC);
			goto cleanup_and_exit;
		}
		set_buffer_verified(bh);
		i = search_dirblock(bh, dir, fname,
			    block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
		if (i == 1) {
			EXT4_I(dir)->i_dir_start_lookup = block;
			ret = bh;
			goto cleanup_and_exit;
		} else {
			brelse(bh);
			if (i < 0)
				goto cleanup_and_exit;
		}
	next:
		if (++block >= nblocks)
			block = 0;
	} while (block != start);

	/*
	 * If the directory has grown while we were searching, then
	 * search the last part of the directory before giving up.
	 */
	block = nblocks;
	nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb);
	if (block < nblocks) {
		start = 0;
		goto restart;
	}

cleanup_and_exit:
	/* Clean up the read-ahead blocks */
	for (; ra_ptr < ra_max; ra_ptr++)
		brelse(bh_use[ra_ptr]);
	return ret;
}

ext4: fix potential negative array index in do_split()

If for any reason a directory passed to do_split() does not have enough
active entries to exceed half the size of the block, we can end up
iterating over all "count" entries without finding a split point.

In this case, count == move, and split will be zero, and we will
attempt a negative index into map[].

Guard against this by detecting this case, and falling back to
split-to-half-of-count instead; in this case we will still have
plenty of space (> half blocksize) in each split block.

Fixes: ef2b02d3e617 ("ext34: ensure do_split leaves enough free space in both blocks")
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/f53e246b-647c-64bb-16ec-135383c70ad7@redhat.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>

#define NAMEI_RA_SIZE	     (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

#define dxtrace(command) command

#define dxtrace(command)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);


static void dx_sort_map (struct dx_map_entry *map, unsigned count)
{
	struct dx_map_entry *p, *q, *top = map + count - 1;
	int more;
	/* Combsort until bubble sort doesn't suck */
	while (count > 2) {
		count = count*10/13;
		if (count - 9 < 2) /* 9, 10 -> 11 */
			count = 11;
		for (p = top, q = p - count; q >= map; p--, q--)
			if (p->hash < q->hash)
				swap(*p, *q);
	}
	/* Garden variety bubble sort */
	do {
		more = 0;
		q = top;
		while (q-- > map) {
			if (q[1].hash >= q[0].hash)
				continue;
			swap(*(q+1), *q);
			more = 1;
		}
	} while(more);
}


static void ext4_inc_count(handle_t *handle, struct inode *inode)
{
	inc_nlink(inode);
	if (is_dx(inode) &&
	    (inode->i_nlink > EXT4_LINK_MAX || inode->i_nlink == 2))
		set_nlink(inode, 1);
}

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);

#define warn_no_space_for_csum(inode)					\
	__warn_no_space_for_csum((inode), __func__, __LINE__)

struct stats
{
	unsigned names;
	unsigned space;
	unsigned bcount;
}


static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent)
{
	int retval;

	ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode,
					      &retval, &ent->parent_de,
					      &ent->dir_inlined);
	if (!ent->dir_bh)
		return retval;
	if (le32_to_cpu(ent->parent_de->inode) != ent->dir->i_ino)
		return -EFSCORRUPTED;
	BUFFER_TRACE(ent->dir_bh, "get_write_access");
	return ext4_journal_get_write_access(handle, ent->dir_bh);
}


static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
	handle_t *handle;
	struct inode *inode;
	int err, retries = 0;

	err = dquot_initialize(dir);
	if (err)
		return err;

retry:
	inode = ext4_new_inode_start_handle(dir, mode,
					    NULL, 0, NULL,
					    EXT4_HT_DIR,
			EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
			  4 + EXT4_XATTR_TRANS_BLOCKS);
	handle = ext4_journal_current_handle();
	err = PTR_ERR(inode);
	if (!IS_ERR(inode)) {
		inode->i_op = &ext4_file_inode_operations;
		inode->i_fop = &ext4_file_operations;
		ext4_set_aops(inode);
		d_tmpfile(dentry, inode);
		err = ext4_orphan_add(handle, inode);
		if (err)
			goto err_unlock_inode;
		mark_inode_dirty(inode);
		unlock_new_inode(inode);
	}
	if (handle)
		ext4_journal_stop(handle);
	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
		goto retry;
	return err;
err_unlock_inode:
	ext4_journal_stop(handle);
	unlock_new_inode(inode);
	return err;
}


static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
			       struct ext4_dir_entry *de)
{
	struct super_block *sb = dir->i_sb;

	if (!is_dx(dir))
		return 0;
	if (block == 0)
		return 1;
	if (de->inode == 0 &&
	    ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize) ==
			sb->s_blocksize)
		return 1;
	return 0;
}


static inline int ext4_handle_dirty_dx_node(handle_t *handle,
					    struct inode *inode,
					    struct buffer_head *bh)
{
	ext4_dx_csum_set(inode, (struct ext4_dir_entry *)bh->b_data);
	return ext4_handle_dirty_metadata(handle, inode, bh);
}


int ext4_find_dest_de(struct inode *dir, struct inode *inode,
		      struct buffer_head *bh,
		      void *buf, int buf_size,
		      struct ext4_filename *fname,
		      struct ext4_dir_entry_2 **dest_de)
{
	struct ext4_dir_entry_2 *de;
	unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname));
	int nlen, rlen;
	unsigned int offset = 0;
	char *top;

	de = (struct ext4_dir_entry_2 *)buf;
	top = buf + buf_size - reclen;
	while ((char *) de <= top) {
		if (ext4_check_dir_entry(dir, NULL, de, bh,
					 buf, buf_size, offset))
			return -EFSCORRUPTED;
		if (ext4_match(dir, fname, de))
			return -EEXIST;
		nlen = EXT4_DIR_REC_LEN(de->name_len);
		rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
		if ((de->inode ? rlen - nlen : rlen) >= reclen)
			break;
		de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
		offset += rlen;
	}
	if ((char *) de > top)
		return -ENOSPC;

	*dest_de = de;
	return 0;
}


static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode)
{
	struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
	struct dx_entry *entries, *at;
	struct buffer_head *bh;
	struct super_block *sb = dir->i_sb;
	struct ext4_dir_entry_2 *de;
	int restart;
	int err;

again:
	restart = 0;
	frame = dx_probe(fname, dir, NULL, frames);
	if (IS_ERR(frame))
		return PTR_ERR(frame);
	entries = frame->entries;
	at = frame->at;
	bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT_HTREE);
	if (IS_ERR(bh)) {
		err = PTR_ERR(bh);
		bh = NULL;
		goto cleanup;
	}

	BUFFER_TRACE(bh, "get_write_access");
	err = ext4_journal_get_write_access(handle, bh);
	if (err)
		goto journal_error;

	err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh);
	if (err != -ENOSPC)
		goto cleanup;

	err = 0;
	/* Block full, should compress but for now just split */
	dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n",
		       dx_get_count(entries), dx_get_limit(entries)));
	/* Need to split index? */
	if (dx_get_count(entries) == dx_get_limit(entries)) {
		ext4_lblk_t newblock;
		int levels = frame - frames + 1;
		unsigned int icount;
		int add_level = 1;
		struct dx_entry *entries2;
		struct dx_node *node2;
		struct buffer_head *bh2;

		while (frame > frames) {
			if (dx_get_count((frame - 1)->entries) <
			    dx_get_limit((frame - 1)->entries)) {
				add_level = 0;
				break;
			}
			frame--; /* split higher index block */
			at = frame->at;
			entries = frame->entries;
			restart = 1;
		}
		if (add_level && levels == ext4_dir_htree_level(sb)) {
			ext4_warning(sb, "Directory (ino: %lu) index full, "
					 "reach max htree level :%d",
					 dir->i_ino, levels);
			if (ext4_dir_htree_level(sb) < EXT4_HTREE_LEVEL) {
				ext4_warning(sb, "Large directory feature is "
						 "not enabled on this "
						 "filesystem");
			}
			err = -ENOSPC;
			goto cleanup;
		}
		icount = dx_get_count(entries);
		bh2 = ext4_append(handle, dir, &newblock);
		if (IS_ERR(bh2)) {
			err = PTR_ERR(bh2);
			goto cleanup;
		}
		node2 = (struct dx_node *)(bh2->b_data);
		entries2 = node2->entries;
		memset(&node2->fake, 0, sizeof(struct fake_dirent));
		node2->fake.rec_len = ext4_rec_len_to_disk(sb->s_blocksize,
							   sb->s_blocksize);
		BUFFER_TRACE(frame->bh, "get_write_access");
		err = ext4_journal_get_write_access(handle, frame->bh);
		if (err)
			goto journal_error;
		if (!add_level) {
			unsigned icount1 = icount/2, icount2 = icount - icount1;
			unsigned hash2 = dx_get_hash(entries + icount1);
			dxtrace(printk(KERN_DEBUG "Split index %i/%i\n",
				       icount1, icount2));

			BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */
			err = ext4_journal_get_write_access(handle,
							     (frame - 1)->bh);
			if (err)
				goto journal_error;

			memcpy((char *) entries2, (char *) (entries + icount1),
			       icount2 * sizeof(struct dx_entry));
			dx_set_count(entries, icount1);
			dx_set_count(entries2, icount2);
			dx_set_limit(entries2, dx_node_limit(dir));

			/* Which index block gets the new entry? */
			if (at - entries >= icount1) {
				frame->at = at = at - entries - icount1 + entries2;
				frame->entries = entries = entries2;
				swap(frame->bh, bh2);
			}
			dx_insert_block((frame - 1), hash2, newblock);
			dxtrace(dx_show_index("node", frame->entries));
			dxtrace(dx_show_index("node",
			       ((struct dx_node *) bh2->b_data)->entries));
			err = ext4_handle_dirty_dx_node(handle, dir, bh2);
			if (err)
				goto journal_error;
			brelse (bh2);
			err = ext4_handle_dirty_dx_node(handle, dir,
						   (frame - 1)->bh);
			if (err)
				goto journal_error;
			if (restart) {
				err = ext4_handle_dirty_dx_node(handle, dir,
							   frame->bh);
				goto journal_error;
			}
		} else {
			struct dx_root *dxroot;
			memcpy((char *) entries2, (char *) entries,
			       icount * sizeof(struct dx_entry));
			dx_set_limit(entries2, dx_node_limit(dir));

			/* Set up root */
			dx_set_count(entries, 1);
			dx_set_block(entries + 0, newblock);
			dxroot = (struct dx_root *)frames[0].bh->b_data;
			dxroot->info.indirect_levels += 1;
			dxtrace(printk(KERN_DEBUG
				       "Creating %d level index...\n",
				       dxroot->info.indirect_levels));
			err = ext4_handle_dirty_dx_node(handle, dir, frame->bh);
			if (err)
				goto journal_error;
			err = ext4_handle_dirty_dx_node(handle, dir, bh2);
			brelse(bh2);
			restart = 1;
			goto journal_error;
		}
	}
	de = do_split(handle, dir, &bh, frame, &fname->hinfo);
	if (IS_ERR(de)) {
		err = PTR_ERR(de);
		goto cleanup;
	}
	err = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
	goto cleanup;

journal_error:
	ext4_std_error(dir->i_sb, err); /* this is a no-op if err == 0 */
cleanup:
	brelse(bh);
	dx_release(frames);
	/* @restart is true means htree-path has been changed, we need to
	 * repeat dx_probe() to find out valid htree-path
	 */
	if (restart && err == 0)
		goto again;
	return err;
}

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

typedef enum {
	EITHER, INDEX, DIRENT, DIRENT_HTREE
} dirblock_type_t;

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

#define dxtrace(command) command

#define dxtrace(command)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);


static int ext4_link(struct dentry *old_dentry,
		     struct inode *dir, struct dentry *dentry)
{
	handle_t *handle;
	struct inode *inode = d_inode(old_dentry);
	int err, retries = 0;

	if (inode->i_nlink >= EXT4_LINK_MAX)
		return -EMLINK;

	err = fscrypt_prepare_link(old_dentry, dir, dentry);
	if (err)
		return err;

	if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
	    (!projid_eq(EXT4_I(dir)->i_projid,
			EXT4_I(old_dentry->d_inode)->i_projid)))
		return -EXDEV;

	err = dquot_initialize(dir);
	if (err)
		return err;

retry:
	handle = ext4_journal_start(dir, EXT4_HT_DIR,
		(EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
		 EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 1);
	if (IS_ERR(handle))
		return PTR_ERR(handle);

	if (IS_DIRSYNC(dir))
		ext4_handle_sync(handle);

	inode->i_ctime = current_time(inode);
	ext4_inc_count(handle, inode);
	ihold(inode);

	err = ext4_add_entry(handle, dentry, inode);
	if (!err) {
		err = ext4_mark_inode_dirty(handle, inode);
		/* this can happen only for tmpfile being
		 * linked the first time
		 */
		if (inode->i_nlink == 1)
			ext4_orphan_del(handle, inode);
		d_instantiate(dentry, inode);
	} else {
		drop_nlink(inode);
		iput(inode);
	}
	ext4_journal_stop(handle);
	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
		goto retry;
	return err;
}


static struct buffer_head *ext4_lookup_entry(struct inode *dir,
					     struct dentry *dentry,
					     struct ext4_dir_entry_2 **res_dir)
{
	int err;
	struct ext4_filename fname;
	struct buffer_head *bh;

	err = ext4_fname_prepare_lookup(dir, dentry, &fname);
	if (err == -ENOENT)
		return NULL;
	if (err)
		return ERR_PTR(err);

	bh = __ext4_find_entry(dir, &fname, res_dir, NULL);

	ext4_fname_free_filename(&fname);
	return bh;
}


static inline ext4_lblk_t dx_get_block(struct dx_entry *entry)
{
	return le32_to_cpu(entry->block) & 0x0fffffff;
}

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);

struct stats
{
	unsigned names;
	unsigned space;
	unsigned bcount;
}


dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count,
		unsigned blocksize)
{
	unsigned rec_len = 0;

	while (count--) {
		struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)
						(from + (map->offs<<2));
		rec_len = EXT4_DIR_REC_LEN(de->name_len);
		memcpy (to, de, rec_len);
		((struct ext4_dir_entry_2 *) to)->rec_len =
				ext4_rec_len_to_disk(rec_len, blocksize);
		de->inode = 0;
		map++;
		to += rec_len;
	}
	return (struct ext4_dir_entry_2 *) (to - rec_len);
}


struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
			     struct dx_entry *entries, int levels)
{
	unsigned blocksize = dir->i_sb->s_blocksize;
	unsigned count = dx_get_count(entries), names = 0, space = 0, i;
	unsigned bcount = 0;
	struct buffer_head *bh;
	printk("%i indexed blocks...\n", count);
	for (i = 0; i < count; i++, entries++)
	{
		ext4_lblk_t block = dx_get_block(entries);
		ext4_lblk_t hash  = i ? dx_get_hash(entries): 0;
		u32 range = i < count - 1? (dx_get_hash(entries + 1) - hash): ~hash;
		struct stats stats;
		printk("%s%3u:%03u hash %8x/%8x ",levels?"":"   ", i, block, hash, range);
		bh = ext4_bread(NULL,dir, block, 0);
		if (!bh || IS_ERR(bh))
			continue;
		stats = levels?
		   dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
		   dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *)
			bh->b_data, blocksize, 0);
		names += stats.names;
		space += stats.space;
		bcount += stats.bcount;
		brelse(bh);
	}
	if (bcount)
		printk(KERN_DEBUG "%snames %u, fullness %u (%u%%)\n",
		       levels ? "" : "   ", names, space/bcount,
		       (space/bcount)*100/blocksize);
	return (struct stats) { names, space, bcount};
}


static int ext4_init_new_dir(handle_t *handle, struct inode *dir,
			     struct inode *inode)
{
	struct buffer_head *dir_block = NULL;
	struct ext4_dir_entry_2 *de;
	ext4_lblk_t block = 0;
	unsigned int blocksize = dir->i_sb->s_blocksize;
	int csum_size = 0;
	int err;

	if (ext4_has_metadata_csum(dir->i_sb))
		csum_size = sizeof(struct ext4_dir_entry_tail);

	if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
		err = ext4_try_create_inline_dir(handle, dir, inode);
		if (err < 0 && err != -ENOSPC)
			goto out;
		if (!err)
			goto out;
	}

	inode->i_size = 0;
	dir_block = ext4_append(handle, inode, &block);
	if (IS_ERR(dir_block))
		return PTR_ERR(dir_block);
	de = (struct ext4_dir_entry_2 *)dir_block->b_data;
	ext4_init_dot_dotdot(inode, de, blocksize, csum_size, dir->i_ino, 0);
	set_nlink(inode, 2);
	if (csum_size)
		ext4_initialize_dirent_tail(dir_block, blocksize);

	BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
	err = ext4_handle_dirty_dirblock(handle, inode, dir_block);
	if (err)
		goto out;
	set_buffer_verified(dir_block);
out:
	brelse(dir_block);
	return err;
}


static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
					       struct ext4_dir_entry *dirent,
					       int *offset)
{
	struct ext4_dir_entry *dp;
	struct dx_root_info *root;
	int count_offset;

	if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb))
		count_offset = 8;
	else if (le16_to_cpu(dirent->rec_len) == 12) {
		dp = (struct ext4_dir_entry *)(((void *)dirent) + 12);
		if (le16_to_cpu(dp->rec_len) !=
		    EXT4_BLOCK_SIZE(inode->i_sb) - 12)
			return NULL;
		root = (struct dx_root_info *)(((void *)dp + 12));
		if (root->reserved_zero ||
		    root->info_length != sizeof(struct dx_root_info))
			return NULL;
		count_offset = 32;
	} else
		return NULL;

	if (offset)
		*offset = count_offset;
	return (struct dx_countlimit *)(((void *)dirent) + count_offset);
}


static int htree_dirblock_to_tree(struct file *dir_file,
				  struct inode *dir, ext4_lblk_t block,
				  struct dx_hash_info *hinfo,
				  __u32 start_hash, __u32 start_minor_hash)
{
	struct buffer_head *bh;
	struct ext4_dir_entry_2 *de, *top;
	int err = 0, count = 0;
	struct fscrypt_str fname_crypto_str = FSTR_INIT(NULL, 0), tmp_str;

	dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
							(unsigned long)block));
	bh = ext4_read_dirblock(dir, block, DIRENT_HTREE);
	if (IS_ERR(bh))
		return PTR_ERR(bh);

	de = (struct ext4_dir_entry_2 *) bh->b_data;
	top = (struct ext4_dir_entry_2 *) ((char *) de +
					   dir->i_sb->s_blocksize -
					   EXT4_DIR_REC_LEN(0));
	/* Check if the directory is encrypted */
	if (IS_ENCRYPTED(dir)) {
		err = fscrypt_get_encryption_info(dir);
		if (err < 0) {
			brelse(bh);
			return err;
		}
		err = fscrypt_fname_alloc_buffer(dir, EXT4_NAME_LEN,
						     &fname_crypto_str);
		if (err < 0) {
			brelse(bh);
			return err;
		}
	}

	for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
		if (ext4_check_dir_entry(dir, NULL, de, bh,
				bh->b_data, bh->b_size,
				(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
					 + ((char *)de - bh->b_data))) {
			/* silently ignore the rest of the block */
			break;
		}
		ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
		if ((hinfo->hash < start_hash) ||
		    ((hinfo->hash == start_hash) &&
		     (hinfo->minor_hash < start_minor_hash)))
			continue;
		if (de->inode == 0)
			continue;
		if (!IS_ENCRYPTED(dir)) {
			tmp_str.name = de->name;
			tmp_str.len = de->name_len;
			err = ext4_htree_store_dirent(dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
				   &tmp_str);
		} else {
			int save_len = fname_crypto_str.len;
			struct fscrypt_str de_name = FSTR_INIT(de->name,
								de->name_len);

			/* Directory is encrypted */
			err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
					hinfo->minor_hash, &de_name,
					&fname_crypto_str);
			if (err) {
				count = err;
				goto errout;
			}
			err = ext4_htree_store_dirent(dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
					&fname_crypto_str);
			fname_crypto_str.len = save_len;
		}
		if (err != 0) {
			count = err;
			goto errout;
		}
		count++;
	}
errout:
	brelse(bh);
	fscrypt_fname_free_buffer(&fname_crypto_str);
	return count;
}


static void ext4_dirblock_csum_set(struct inode *inode,
				 struct buffer_head *bh)
{
	struct ext4_dir_entry_tail *t;

	if (!ext4_has_metadata_csum(inode->i_sb))
		return;

	t = get_dirent_tail(inode, bh);
	if (!t) {
		warn_no_space_for_csum(inode);
		return;
	}

	t->det_checksum = ext4_dirblock_csum(inode, bh->b_data,
					     (char *)t - bh->b_data);
}


static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
			       int force_reread)
{
	int retval;
	/*
	 * ent->de could have moved from under us during htree split, so make
	 * sure that we are deleting the right entry.  We might also be pointing
	 * to a stale entry in the unused part of ent->bh so just checking inum
	 * and the name isn't enough.
	 */
	if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
	    ent->de->name_len != ent->dentry->d_name.len ||
	    strncmp(ent->de->name, ent->dentry->d_name.name,
		    ent->de->name_len) ||
	    force_reread) {
		retval = ext4_find_delete_entry(handle, ent->dir,
						&ent->dentry->d_name);
	} else {
		retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh);
		if (retval == -ENOENT) {
			retval = ext4_find_delete_entry(handle, ent->dir,
							&ent->dentry->d_name);
		}
	}

	if (retval) {
		ext4_warning_inode(ent->dir,
				   "Deleting old file: nlink %d, error=%d",
				   ent->dir->i_nlink, retval);
	}
}


int ext4_generic_delete_entry(handle_t *handle,
			      struct inode *dir,
			      struct ext4_dir_entry_2 *de_del,
			      struct buffer_head *bh,
			      void *entry_buf,
			      int buf_size,
			      int csum_size)
{
	struct ext4_dir_entry_2 *de, *pde;
	unsigned int blocksize = dir->i_sb->s_blocksize;
	int i;

	i = 0;
	pde = NULL;
	de = (struct ext4_dir_entry_2 *)entry_buf;
	while (i < buf_size - csum_size) {
		if (ext4_check_dir_entry(dir, NULL, de, bh,
					 bh->b_data, bh->b_size, i))
			return -EFSCORRUPTED;
		if (de == de_del)  {
			if (pde)
				pde->rec_len = ext4_rec_len_to_disk(
					ext4_rec_len_from_disk(pde->rec_len,
							       blocksize) +
					ext4_rec_len_from_disk(de->rec_len,
							       blocksize),
					blocksize);
			else
				de->inode = 0;
			inode_inc_iversion(dir);
			return 0;
		}
		i += ext4_rec_len_from_disk(de->rec_len, blocksize);
		pde = de;
		de = ext4_next_entry(de, blocksize);
	}
	return -ENOENT;
}


dx_probe(struct ext4_filename *fname, struct inode *dir,
	 struct dx_hash_info *hinfo, struct dx_frame *frame_in)
{
	unsigned count, indirect;
	struct dx_entry *at, *entries, *p, *q, *m;
	struct dx_root *root;
	struct dx_frame *frame = frame_in;
	struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR);
	u32 hash;

	memset(frame_in, 0, EXT4_HTREE_LEVEL * sizeof(frame_in[0]));
	frame->bh = ext4_read_dirblock(dir, 0, INDEX);
	if (IS_ERR(frame->bh))
		return (struct dx_frame *) frame->bh;

	root = (struct dx_root *) frame->bh->b_data;
	if (root->info.hash_version != DX_HASH_TEA &&
	    root->info.hash_version != DX_HASH_HALF_MD4 &&
	    root->info.hash_version != DX_HASH_LEGACY) {
		ext4_warning_inode(dir, "Unrecognised inode hash code %u",
				   root->info.hash_version);
		goto fail;
	}
	if (fname)
		hinfo = &fname->hinfo;
	hinfo->hash_version = root->info.hash_version;
	if (hinfo->hash_version <= DX_HASH_TEA)
		hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
	hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
	if (fname && fname_name(fname))
		ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo);
	hash = hinfo->hash;

	if (root->info.unused_flags & 1) {
		ext4_warning_inode(dir, "Unimplemented hash flags: %#06x",
				   root->info.unused_flags);
		goto fail;
	}

	indirect = root->info.indirect_levels;
	if (indirect >= ext4_dir_htree_level(dir->i_sb)) {
		ext4_warning(dir->i_sb,
			     "Directory (ino: %lu) htree depth %#06x exceed"
			     "supported value", dir->i_ino,
			     ext4_dir_htree_level(dir->i_sb));
		if (ext4_dir_htree_level(dir->i_sb) < EXT4_HTREE_LEVEL) {
			ext4_warning(dir->i_sb, "Enable large directory "
						"feature to access it");
		}
		goto fail;
	}

	entries = (struct dx_entry *)(((char *)&root->info) +
				      root->info.info_length);

	if (dx_get_limit(entries) != dx_root_limit(dir,
						   root->info.info_length)) {
		ext4_warning_inode(dir, "dx entry: limit %u != root limit %u",
				   dx_get_limit(entries),
				   dx_root_limit(dir, root->info.info_length));
		goto fail;
	}

	dxtrace(printk("Look up %x", hash));
	while (1) {
		count = dx_get_count(entries);
		if (!count || count > dx_get_limit(entries)) {
			ext4_warning_inode(dir,
					   "dx entry: count %u beyond limit %u",
					   count, dx_get_limit(entries));
			goto fail;
		}

		p = entries + 1;
		q = entries + count - 1;
		while (p <= q) {
			m = p + (q - p) / 2;
			dxtrace(printk(KERN_CONT "."));
			if (dx_get_hash(m) > hash)
				q = m - 1;
			else
				p = m + 1;
		}

		if (0) { // linear search cross check
			unsigned n = count - 1;
			at = entries;
			while (n--)
			{
				dxtrace(printk(KERN_CONT ","));
				if (dx_get_hash(++at) > hash)
				{
					at--;
					break;
				}
			}
			assert (at == p - 1);
		}

		at = p - 1;
		dxtrace(printk(KERN_CONT " %x->%u\n",
			       at == entries ? 0 : dx_get_hash(at),
			       dx_get_block(at)));
		frame->entries = entries;
		frame->at = at;
		if (!indirect--)
			return frame;
		frame++;
		frame->bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX);
		if (IS_ERR(frame->bh)) {
			ret_err = (struct dx_frame *) frame->bh;
			frame->bh = NULL;
			goto fail;
		}
		entries = ((struct dx_node *) frame->bh->b_data)->entries;

		if (dx_get_limit(entries) != dx_node_limit(dir)) {
			ext4_warning_inode(dir,
				"dx entry: limit %u != node limit %u",
				dx_get_limit(entries), dx_node_limit(dir));
			goto fail;
		}
	}
fail:
	while (frame >= frame_in) {
		brelse(frame->bh);
		frame--;
	}

	if (ret_err == ERR_PTR(ERR_BAD_DX_DIR))
		ext4_warning_inode(dir,
			"Corrupt directory, running e2fsck is recommended");
	return ret_err;
}

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

typedef enum {
	EITHER, INDEX, DIRENT, DIRENT_HTREE
} dirblock_type_t;

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

#define assert(test) J_ASSERT(test)

#define dxtrace(command) command

#define dxtrace(command)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);


int ext4_orphan_del(handle_t *handle, struct inode *inode)
{
	struct list_head *prev;
	struct ext4_inode_info *ei = EXT4_I(inode);
	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
	__u32 ino_next;
	struct ext4_iloc iloc;
	int err = 0;

	if (!sbi->s_journal && !(sbi->s_mount_state & EXT4_ORPHAN_FS))
		return 0;

	WARN_ON_ONCE(!(inode->i_state & (I_NEW | I_FREEING)) &&
		     !inode_is_locked(inode));
	/* Do this quick check before taking global s_orphan_lock. */
	if (list_empty(&ei->i_orphan))
		return 0;

	if (handle) {
		/* Grab inode buffer early before taking global s_orphan_lock */
		err = ext4_reserve_inode_write(handle, inode, &iloc);
	}

	mutex_lock(&sbi->s_orphan_lock);
	jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino);

	prev = ei->i_orphan.prev;
	list_del_init(&ei->i_orphan);

	/* If we're on an error path, we may not have a valid
	 * transaction handle with which to update the orphan list on
	 * disk, but we still need to remove the inode from the linked
	 * list in memory. */
	if (!handle || err) {
		mutex_unlock(&sbi->s_orphan_lock);
		goto out_err;
	}

	ino_next = NEXT_ORPHAN(inode);
	if (prev == &sbi->s_orphan) {
		jbd_debug(4, "superblock will point to %u\n", ino_next);
		BUFFER_TRACE(sbi->s_sbh, "get_write_access");
		err = ext4_journal_get_write_access(handle, sbi->s_sbh);
		if (err) {
			mutex_unlock(&sbi->s_orphan_lock);
			goto out_brelse;
		}
		sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
		mutex_unlock(&sbi->s_orphan_lock);
		err = ext4_handle_dirty_super(handle, inode->i_sb);
	} else {
		struct ext4_iloc iloc2;
		struct inode *i_prev =
			&list_entry(prev, struct ext4_inode_info, i_orphan)->vfs_inode;

		jbd_debug(4, "orphan inode %lu will point to %u\n",
			  i_prev->i_ino, ino_next);
		err = ext4_reserve_inode_write(handle, i_prev, &iloc2);
		if (err) {
			mutex_unlock(&sbi->s_orphan_lock);
			goto out_brelse;
		}
		NEXT_ORPHAN(i_prev) = ino_next;
		err = ext4_mark_iloc_dirty(handle, i_prev, &iloc2);
		mutex_unlock(&sbi->s_orphan_lock);
	}
	if (err)
		goto out_brelse;
	NEXT_ORPHAN(inode) = 0;
	err = ext4_mark_iloc_dirty(handle, inode, &iloc);
out_err:
	ext4_std_error(inode->i_sb, err);
	return err;

out_brelse:
	brelse(iloc.bh);
	goto out_err;
}


static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
						ext4_lblk_t block,
						dirblock_type_t type,
						const char *func,
						unsigned int line)
{
	struct buffer_head *bh;
	struct ext4_dir_entry *dirent;
	int is_dx_block = 0;

	if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
		bh = ERR_PTR(-EIO);
	else
		bh = ext4_bread(NULL, inode, block, 0);
	if (IS_ERR(bh)) {
		__ext4_warning(inode->i_sb, func, line,
			       "inode #%lu: lblock %lu: comm %s: "
			       "error %ld reading directory block",
			       inode->i_ino, (unsigned long)block,
			       current->comm, PTR_ERR(bh));

		return bh;
	}
	if (!bh && (type == INDEX || type == DIRENT_HTREE)) {
		ext4_error_inode(inode, func, line, block,
				 "Directory hole found for htree %s block",
				 (type == INDEX) ? "index" : "leaf");
		return ERR_PTR(-EFSCORRUPTED);
	}
	if (!bh)
		return NULL;
	dirent = (struct ext4_dir_entry *) bh->b_data;
	/* Determine whether or not we have an index block */
	if (is_dx(inode)) {
		if (block == 0)
			is_dx_block = 1;
		else if (ext4_rec_len_from_disk(dirent->rec_len,
						inode->i_sb->s_blocksize) ==
			 inode->i_sb->s_blocksize)
			is_dx_block = 1;
	}
	if (!is_dx_block && type == INDEX) {
		ext4_error_inode(inode, func, line, block,
		       "directory leaf block found instead of index block");
		brelse(bh);
		return ERR_PTR(-EFSCORRUPTED);
	}
	if (!ext4_has_metadata_csum(inode->i_sb) ||
	    buffer_verified(bh))
		return bh;

	/*
	 * An empty leaf block can get mistaken for a index block; for
	 * this reason, we can only check the index checksum when the
	 * caller is sure it should be an index block.
	 */
	if (is_dx_block && type == INDEX) {
		if (ext4_dx_csum_verify(inode, dirent) &&
		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
			set_buffer_verified(bh);
		else {
			ext4_error_inode_err(inode, func, line, block,
					     EFSBADCRC,
					     "Directory index failed checksum");
			brelse(bh);
			return ERR_PTR(-EFSBADCRC);
		}
	}
	if (!is_dx_block) {
		if (ext4_dirblock_csum_verify(inode, bh) &&
		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
			set_buffer_verified(bh);
		else {
			ext4_error_inode_err(inode, func, line, block,
					     EFSBADCRC,
					     "Directory block failed checksum");
			brelse(bh);
			return ERR_PTR(-EFSBADCRC);
		}
	}
	return bh;
}

static int ext4_dx_csum_verify(struct inode *inode,
			       struct ext4_dir_entry *dirent);

typedef enum {
	EITHER, INDEX, DIRENT, DIRENT_HTREE
} dirblock_type_t;

#define ext4_read_dirblock(inode, block, type) \
	__ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)

struct fake_dirent
{
	__le32 inode;
	__le16 rec_len;
	u8 name_len;
	u8 file_type;
}

struct dx_countlimit
{
	__le16 limit;
	__le16 count;
}

struct dx_entry
{
	__le32 hash;
	__le32 block;
}

struct dx_root
{
	struct fake_dirent dot;
	char dot_name[4];
	struct fake_dirent dotdot;
	char dotdot_name[4];
	struct dx_root_info
	{
		__le32 reserved_zero;
		u8 hash_version;
		u8 info_length; /* 8 */
		u8 indirect_levels;
		u8 unused_flags;
	}
	info;
	struct dx_entry	entries[];
}

struct dx_node
{
	struct fake_dirent fake;
	struct dx_entry	entries[];
}

struct dx_frame
{
	struct buffer_head *bh;
	struct dx_entry *entries;
	struct dx_entry *at;
}

struct dx_map_entry
{
	u32 hash;
	u16 offs;
	u16 size;
}

struct dx_tail {
	u32 dt_reserved;
	__le32 dt_checksum;	/* crc32c(uuid+inum+dirblock) */
}

static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);

static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);

static inline unsigned dx_get_hash(struct dx_entry *entry);

static void dx_set_hash(struct dx_entry *entry, unsigned value);

static unsigned dx_get_count(struct dx_entry *entries);

static unsigned dx_get_limit(struct dx_entry *entries);

static void dx_set_count(struct dx_entry *entries, unsigned value);

static void dx_set_limit(struct dx_entry *entries, unsigned value);

static unsigned dx_root_limit(struct inode *dir, unsigned infosize);

static unsigned dx_node_limit(struct inode *dir);

static struct dx_frame *dx_probe(struct ext4_filename *fname,
				 struct inode *dir,
				 struct dx_hash_info *hinfo,
				 struct dx_frame *frame);

static void dx_release(struct dx_frame *frames);

static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
		       unsigned blocksize, struct dx_hash_info *hinfo,
		       struct dx_map_entry map[]);

static void dx_sort_map(struct dx_map_entry *map, unsigned count);

static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
		struct dx_map_entry *offsets, int count, unsigned blocksize);

static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);

static void dx_insert_block(struct dx_frame *frame,
					u32 hash, ext4_lblk_t block);

static int ext4_htree_next_block(struct inode *dir, __u32 hash,
				 struct dx_frame *frame,
				 struct dx_frame *frames,
				 __u32 *start_hash);

static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		struct ext4_filename *fname,
		struct ext4_dir_entry_2 **res_dir);

static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir, struct inode *inode);

#define warn_no_space_for_csum(inode)					\
	__warn_no_space_for_csum((inode), __func__, __LINE__)


static inline bool ext4_match(const struct inode *parent,
			      const struct ext4_filename *fname,
			      const struct ext4_dir_entry_2 *de)
{
	struct fscrypt_name f;
#ifdef CONFIG_UNICODE
	const struct qstr entry = {.name = de->name, .len = de->name_len};
#endif

	if (!de->inode)
		return false;

	f.usr_fname = fname->usr_fname;
	f.disk_name = fname->disk_name;
#ifdef CONFIG_FS_ENCRYPTION
	f.crypto_buf = fname->crypto_buf;
#endif

#ifdef CONFIG_UNICODE
	if (EXT4_SB(parent->i_sb)->s_encoding && IS_CASEFOLDED(parent)) {
		if (fname->cf_name.name) {
			struct qstr cf = {.name = fname->cf_name.name,
					  .len = fname->cf_name.len};
			return !ext4_ci_compare(parent, &cf, &entry, true);
		}
		return !ext4_ci_compare(parent, fname->usr_fname, &entry,
					false);
	}
#endif

	return fscrypt_match_name(&f, de->name, de->name_len);
}


struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
			  struct ext4_dir_entry_2 *de,
			  int blocksize, int csum_size,
			  unsigned int parent_ino, int dotdot_real_len)
{
	de->inode = cpu_to_le32(inode->i_ino);
	de->name_len = 1;
	de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len),
					   blocksize);
	strcpy(de->name, ".");
	ext4_set_de_type(inode->i_sb, de, S_IFDIR);

	de = ext4_next_entry(de, blocksize);
	de->inode = cpu_to_le32(parent_ino);
	de->name_len = 2;
	if (!dotdot_real_len)
		de->rec_len = ext4_rec_len_to_disk(blocksize -
					(csum_size + EXT4_DIR_REC_LEN(1)),
					blocksize);
	else
		de->rec_len = ext4_rec_len_to_disk(
				EXT4_DIR_REC_LEN(de->name_len), blocksize);
	strcpy(de->name, "..");
	ext4_set_de_type(inode->i_sb, de, S_IFDIR);

	return ext4_next_entry(de, blocksize);
}


int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
		    struct inode *dir, struct ext4_filename *fname,
		    unsigned int offset, struct ext4_dir_entry_2 **res_dir)
{
	struct ext4_dir_entry_2 * de;
	char * dlimit;
	int de_len;

	de = (struct ext4_dir_entry_2 *)search_buf;
	dlimit = search_buf + buf_size;
	while ((char *) de < dlimit) {
		/* this code is executed quadratically often */
		/* do minimal checking `by hand' */
		if ((char *) de + de->name_len <= dlimit &&
		    ext4_match(dir, fname, de)) {
			/* found a match - just to be sure, do
			 * a full check */
			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
						 bh->b_size, offset))
				return -1;
			*res_dir = de;
			return 1;
		}
		/* prevent looping on a bad block */
		de_len = ext4_rec_len_from_disk(de->rec_len,
						dir->i_sb->s_blocksize);
		if (de_len <= 0)
			return -1;
		offset += de_len;
		de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
	}
	return 0;
}