copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &flags)
{
    debugs(11, 5, "httpBuildRequestHeader: " << e->name << ": " << e->value );

    switch (e->id) {

    /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */

    case Http::HdrType::PROXY_AUTHORIZATION:
        /** \par Proxy-Authorization:
         * Only pass on proxy authentication to peers for which
         * authentication forwarding is explicitly enabled
         */
        if (!flags.originpeer && flags.proxying && request->peer_login &&
                (strcmp(request->peer_login, "PASS") == 0 ||
                 strcmp(request->peer_login, "PROXYPASS") == 0 ||
                 strcmp(request->peer_login, "PASSTHRU") == 0)) {
            hdr_out->addEntry(e->clone());
        }
        break;

    /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid does not pass on. */

    case Http::HdrType::CONNECTION:          /** \par Connection: */
    case Http::HdrType::TE:                  /** \par TE: */
    case Http::HdrType::KEEP_ALIVE:          /** \par Keep-Alive: */
    case Http::HdrType::PROXY_AUTHENTICATE:  /** \par Proxy-Authenticate: */
    case Http::HdrType::TRAILER:             /** \par Trailer: */
    case Http::HdrType::UPGRADE:             /** \par Upgrade: */
    case Http::HdrType::TRANSFER_ENCODING:   /** \par Transfer-Encoding: */
        break;

    /** \par OTHER headers I haven't bothered to track down yet. */

    case Http::HdrType::AUTHORIZATION:
        /** \par WWW-Authorization:
         * Pass on WWW authentication */

        if (!flags.originpeer) {
            hdr_out->addEntry(e->clone());
        } else {
            /** \note In accelerators, only forward authentication if enabled
             * (see also httpFixupAuthentication for special cases)
             */
            if (request->peer_login &&
                    (strcmp(request->peer_login, "PASS") == 0 ||
                     strcmp(request->peer_login, "PASSTHRU") == 0 ||
                     strcmp(request->peer_login, "PROXYPASS") == 0)) {
                hdr_out->addEntry(e->clone());
            }
        }

        break;

    case Http::HdrType::HOST:
        /** \par Host:
         * Normally Squid rewrites the Host: header.
         * However, there is one case when we don't: If the URL
         * went through our redirector and the admin configured
         * 'redir_rewrites_host' to be off.
         */
        if (request->peer_domain)
            hdr_out->putStr(Http::HdrType::HOST, request->peer_domain);
        else if (request->flags.redirected && !Config.onoff.redir_rewrites_host)
            hdr_out->addEntry(e->clone());
        else {
            SBuf authority = request->url.authority();
            hdr_out->putStr(Http::HdrType::HOST, authority.c_str());
        }

        break;

    case Http::HdrType::IF_MODIFIED_SINCE:
        /** \par If-Modified-Since:
         * append unless we added our own,
         * but only if cache_miss_revalidate is enabled, or
         *  the request is not cacheable, or
         *  the request contains authentication credentials.
         * \note at most one client's If-Modified-Since header can pass through
         */
        // XXX: need to check and cleanup the auth case so cacheable auth requests get cached.
        if (hdr_out->has(Http::HdrType::IF_MODIFIED_SINCE))
            break;
        else if (Config.onoff.cache_miss_revalidate || !request->flags.cachable || request->flags.auth)
            hdr_out->addEntry(e->clone());
        break;

    case Http::HdrType::IF_NONE_MATCH:
        /** \par If-None-Match:
         * append if the wildcard '*' special case value is present, or
         *   cache_miss_revalidate is disabled, or
         *   the request is not cacheable in this proxy, or
         *   the request contains authentication credentials.
         * \note this header lists a set of responses for the server to elide sending. Squid added values are extending that set.
         */
        // XXX: need to check and cleanup the auth case so cacheable auth requests get cached.
        if (hdr_out->hasListMember(Http::HdrType::IF_MATCH, "*", ',') || Config.onoff.cache_miss_revalidate || !request->flags.cachable || request->flags.auth)
            hdr_out->addEntry(e->clone());
        break;

    case Http::HdrType::MAX_FORWARDS:
        /** \par Max-Forwards:
         * pass only on TRACE or OPTIONS requests */
        if (request->method == Http::METHOD_TRACE || request->method == Http::METHOD_OPTIONS) {
            const int64_t hops = e->getInt64();

            if (hops > 0)
                hdr_out->putInt64(Http::HdrType::MAX_FORWARDS, hops - 1);
        }

        break;

    case Http::HdrType::VIA:
        /** \par Via:
         * If Via is disabled then forward any received header as-is.
         * Otherwise leave for explicit updated addition later. */

        if (!Config.onoff.via)
            hdr_out->addEntry(e->clone());

        break;

    case Http::HdrType::RANGE:

    case Http::HdrType::IF_RANGE:

    case Http::HdrType::REQUEST_RANGE:
        /** \par Range:, If-Range:, Request-Range:
         * Only pass if we accept ranges */
        if (!we_do_ranges)
            hdr_out->addEntry(e->clone());

        break;

    case Http::HdrType::PROXY_CONNECTION: // SHOULD ignore. But doing so breaks things.
        break;

    case Http::HdrType::CONTENT_LENGTH:
        // pass through unless we chunk; also, keeping this away from default
        // prevents request smuggling via Connection: Content-Length tricks
        if (!flags.chunked_request)
            hdr_out->addEntry(e->clone());
        break;

    case Http::HdrType::X_FORWARDED_FOR:

    case Http::HdrType::CACHE_CONTROL:
        /** \par X-Forwarded-For:, Cache-Control:
         * handled specially by Squid, so leave off for now.
         * append these after the loop if needed */
        break;

    case Http::HdrType::FRONT_END_HTTPS:
        /** \par Front-End-Https:
         * Pass thru only if peer is configured with front-end-https */
        if (!flags.front_end_https)
            hdr_out->addEntry(e->clone());

        break;

    default:
        /** \par default.
         * pass on all other header fields
         * which are NOT listed by the special Connection: header. */

        if (strConnection.size()>0 && strListIsMember(&strConnection, e->name.termedBuf(), ',')) {
            debugs(11, 2, "'" << e->name << "' header cropped by Connection: definition");
            return;
        }

        hdr_out->addEntry(e->clone());
    }
}


ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
{
    Must(pic.connection);
    Must(pic.request);
    pinConnection(pic.connection, *pic.request);

    // monitor pinned server connection for remote-end closures.
    startPinnedConnectionMonitoring();

    if (pipeline.empty())
        kick(); // in case clientParseRequests() was blocked by a busy pic.connection
}


void ConnStateData::connStateClosed(const CommCloseCbParams &)
{
    deleteThis("ConnStateData::connStateClosed");
}


operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
{
    return os << pic.connection << ", request=" << pic.request;
}


HttpStateData::start()
{
    if (!sendRequest()) {
        debugs(11, 3, "httpStart: aborted");
        mustStop("HttpStateData::start failed");
        return;
    }

    ++ statCounter.server.all.requests;
    ++ statCounter.server.http.requests;

    /*
     * We used to set the read timeout here, but not any more.
     * Now its set in httpSendComplete() after the full request,
     * including request body, has been written to the server.
     */
}


ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
{
    if (!Comm::IsConnOpen(io.conn))
        return;

    const err_type error = receivedFirstByte_ ? ERR_REQUEST_PARSE_TIMEOUT : ERR_REQUEST_START_TIMEOUT;
    if (tunnelOnError(HttpRequestMethod(), error))
        return;

    /*
    * Just close the connection to not confuse browsers
    * using persistent connections. Some browsers open
    * a connection and then do not use it until much
    * later (presumeably because the request triggering
    * the open has already been completed on another
    * connection)
    */
    debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
    io.conn->close();
}


ConnStateData::startDechunkingRequest()
{
    Must(bodyPipe != NULL);
    debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
    assert(!bodyParser);
    bodyParser = new Http1::TeChunkedParser;
}


ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
{
    // FwdState might repin a failed connection sooner than this close
    // callback is called for the failed connection.
    assert(pinning.serverConnection == io.conn);
    pinning.closeHandler = NULL; // Comm unregisters handlers before calling
    const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
    pinning.serverConnection->noteClosure();
    unpinConnection(false);

    if (sawZeroReply && clientConnection != NULL) {
        debugs(33, 3, "Closing client connection on pinned zero reply.");
        clientConnection->close();
    }

}


HttpStateData::ReuseDecision::make(const HttpStateData::ReuseDecision::Answers ans, const char *why)
{
    answer = ans;
    reason = why;
    return answer;
}


ConnStateData::swanSong()
{
    debugs(33, 2, HERE << clientConnection);
    checkLogging();

    flags.readMore = false;
    clientdbEstablished(clientConnection->remote, -1);  /* decrement */
    pipeline.terminateAll(0);

    // XXX: Closing pinned conn is too harsh: The Client may want to continue!
    unpinConnection(true);

    Server::swanSong(); // closes the client connection

#if USE_AUTH
    // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
    setAuth(NULL, "ConnStateData::SwanSong cleanup");
#endif

    flags.swanSang = true;
}


ConnStateData::handleReadData()
{
    // if we are reading a body, stuff data into the body pipe
    if (bodyPipe != NULL)
        return handleRequestBodyData();
    return true;
}


ConnStateData::isOpen() const
{
    return cbdataReferenceValid(this) && // XXX: checking "this" in a method
           Comm::IsConnOpen(clientConnection) &&
           !fd_table[clientConnection->fd].closing();
}


get_oc_values(
    XOC oc,
    XlcArgList args,
    int num_args)
{
    if (oc->core.resources == NULL)
	return NULL;

    return _XlcGetValues((XPointer) oc, oc->core.resources,
			 oc->core.num_resources, args, num_args, XlcGetMask);
}


destroy_oc(
    XOC oc)
{
    Display *dpy = oc->core.om->core.display;
    XOCGenericPart *gen = XOC_GENERIC(oc);

    if (gen->mbs_to_cs)
	_XlcCloseConverter(gen->mbs_to_cs);

    if (gen->wcs_to_cs)
	_XlcCloseConverter(gen->wcs_to_cs);

    if (gen->utf8_to_cs)
	_XlcCloseConverter(gen->utf8_to_cs);

/* For VW/UDC start */ /* Change 1996.01.8 */
    destroy_fontdata(gen,dpy);
/*
*/
/* For VW/UDC end */

    Xfree(oc->core.base_name_list);
    XFreeStringList(oc->core.font_info.font_name_list);
    Xfree(oc->core.font_info.font_struct_list);
    XFreeStringList(oc->core.missing_list.charset_list);

#ifdef notdef

    Xfree(oc->core.res_name);
    Xfree(oc->core.res_class);
#endif

    Xfree(oc);
}


init_vrotate(
    FontData	font_data,
    int		font_data_count,
    int		type,
    CodeRange	code_range,
    int		code_range_num)
{
    VRotate	vrotate;
    int		i;

    if(type == VROTATE_NONE)
	return (VRotate)NULL;

    vrotate = Xcalloc(font_data_count, sizeof(VRotateRec));
    if(vrotate == (VRotate) NULL)
	return False;

    for(i = 0 ; i < font_data_count ; i++) {
	vrotate[i].charset_name = font_data[i].name;
	vrotate[i].side = font_data[i].side;
	if(type == VROTATE_PART) {
	    vrotate[i].num_cr = code_range_num;
	    vrotate[i].code_range = code_range;
	}
    }

    return vrotate;
}


get_rotate_fontname(
    char *font_name)
{
    char *pattern = NULL, *ptr = NULL;
    char *fields[CHARSET_ENCODING_FIELD];
    char str_pixel[32], str_point[4];
    char *rotate_font_ptr = NULL;
    int pixel_size = 0;
    int field_num = 0, len = 0;

    if(font_name == (char *) NULL || (len = strlen(font_name)) <= 0
       || len > XLFD_MAX_LEN)
	return NULL;

    pattern = strdup(font_name);
    if(!pattern)
	return NULL;

    memset(fields, 0, sizeof(char *) * 14);
    ptr = pattern;
    while(isspace(*ptr)) {
	ptr++;
    }
    if(*ptr == '-')
	ptr++;

    for(field_num = 0 ; field_num < CHARSET_ENCODING_FIELD && ptr && *ptr ;
			ptr++, field_num++) {
	fields[field_num] = ptr;

	if((ptr = strchr(ptr, '-'))) {
	    *ptr = '\0';
	} else {
	    field_num++;	/* Count last field */
	    break;
	}
    }

    if(field_num < CHARSET_ENCODING_FIELD)
	goto free_pattern;

    /* Pixel Size field : fields[6] */
    for(ptr = fields[PIXEL_SIZE_FIELD - 1] ; ptr && *ptr; ptr++) {
	if(!isdigit(*ptr)) {
	    if(*ptr == '['){ /* 960730 */
	        strcpy(pattern, font_name);
		return(pattern);
	    }
	    goto free_pattern;
	}
    }
    pixel_size = atoi(fields[PIXEL_SIZE_FIELD - 1]);
    snprintf(str_pixel, sizeof(str_pixel),
	     "[ 0 ~%d %d 0 ]", pixel_size, pixel_size);
    fields[6] = str_pixel;

    /* Point Size field : fields[7] */
    strcpy(str_point, "*");
    fields[POINT_SIZE_FIELD - 1] = str_point;

    len = 0;
    for (field_num = 0; field_num < CHARSET_ENCODING_FIELD &&
			fields[field_num]; field_num++) {
	len += 1 + strlen(fields[field_num]);
    }

    /* Max XLFD length is 255 */
    if (len > XLFD_MAX_LEN)
	goto free_pattern;

    rotate_font_ptr = Xmalloc(len + 1);
    if(!rotate_font_ptr)
	goto free_pattern;

    rotate_font_ptr[0] = '\0';

    for(field_num = 0 ; field_num < CHARSET_ENCODING_FIELD &&
			fields[field_num] ; field_num++) {
	strcat(rotate_font_ptr, "-");
	strcat(rotate_font_ptr, fields[field_num]);
    }

free_pattern:
    Xfree(pattern);

    return rotate_font_ptr;
}


init_fontset(
    XOC oc)
{
    XOCGenericPart *gen;
    FontSet font_set;
    OMData data;
    int count;

    count = XOM_GENERIC(oc->core.om)->data_num;
    data = XOM_GENERIC(oc->core.om)->data;

    font_set = Xcalloc(count, sizeof(FontSetRec));
    if (font_set == NULL)
	return False;

    gen = XOC_GENERIC(oc);
    gen->font_set_num = count;
    gen->font_set = font_set;

    for ( ; count-- > 0; data++, font_set++) {
	font_set->charset_count = data->charset_count;
	font_set->charset_list = data->charset_list;

	if((font_set->font_data = init_fontdata(data->font_data,
				  data->font_data_count)) == NULL)
	    goto err;
	font_set->font_data_count = data->font_data_count;
	if((font_set->substitute = init_fontdata(data->substitute,
				   data->substitute_num)) == NULL)
	    goto err;
	font_set->substitute_num = data->substitute_num;
	if((font_set->vmap = init_fontdata(data->vmap,
			     data->vmap_num)) == NULL)
	    goto err;
	font_set->vmap_num       = data->vmap_num;

	if(data->vrotate_type != VROTATE_NONE) {
	    /* A vrotate member is specified primary font data */
	    /* as initial value.                               */
	    if((font_set->vrotate = init_vrotate(data->font_data,
						 data->font_data_count,
						 data->vrotate_type,
						 data->vrotate,
						 data->vrotate_num)) == NULL)
		goto err;
	    font_set->vrotate_num = data->font_data_count;
	}
    }
    return True;

err:

    Xfree(font_set->font_data);
    Xfree(font_set->substitute);
    Xfree(font_set->vmap);
    Xfree(font_set->vrotate);
    Xfree(font_set);
    gen->font_set = (FontSet) NULL;
    gen->font_set_num = 0;
    return False;
}


load_font(
    XOC oc)
{
    XOCGenericPart *gen = XOC_GENERIC(oc);
    FontSet font_set = gen->font_set;
    int num = gen->font_set_num;

    for ( ; num-- > 0; font_set++) {
	if (font_set->font_name == NULL)
	    continue;

        if (load_fontset_data (oc, font_set) != True)
	    return False;
#ifndef TESTVERSION
	if(load_fontdata(oc, font_set->font_data,
			 font_set->font_data_count) != True)
	    return False;

	if(load_fontdata(oc, font_set->substitute,
			 font_set->substitute_num) != True)
	    return False;
#endif

/* Add 1996.05.20 */
        if( oc->core.orientation == XOMOrientation_TTB_RTL ||
            oc->core.orientation == XOMOrientation_TTB_LTR ){
	    if (font_set->vpart_initialize == 0) {
	       load_fontdata(oc, font_set->vmap, font_set->vmap_num);
	       load_fontdata(oc, (FontData) font_set->vrotate,
			 font_set->vrotate_num);
                font_set->vpart_initialize = 1;
	    }
        }

	if (font_set->font->min_byte1 || font_set->font->max_byte1)
	    font_set->is_xchar2b = True;
	else
	    font_set->is_xchar2b = False;
    }

    return True;
}


static const char *unquote_token_cstr(Token *t)
{
    if (t->type != TOK_STRING)
	return tok_text(t);

    t->type = TOK_INTERNAL_STRING;

    if (t->len > INLINE_TEXT) {
	char *p = t->text.p.ptr;

	t->len = nasm_unquote_cstr(p, NULL);

	if (t->len <= INLINE_TEXT) {
	    nasm_zero(t->text.a);
	    memcpy(t->text.a, p, t->len);
	    nasm_free(p);
	    return t->text.a;
	} else {
	    return p;
	}
    } else {
	t->len = nasm_unquote_cstr(t->text.a, NULL);
	return t->text.a;
    }
}


static void pp_pre_define(char *definition)
{
    Token *def, *space;
    Line *l;
    char *equals;

    equals = strchr(definition, '=');
    space = new_White(NULL);
    def = new_Token(space, TOK_PREPROC_ID, "%define", 0);
    if (equals)
        *equals = ' ';
    space->next = tokenize(definition);
    if (equals)
        *equals = '=';

    /* We can't predefine a TOK_LOCAL_MACRO for obvious reasons... */
    if (space->next->type != TOK_PREPROC_ID &&
        space->next->type != TOK_ID)
        nasm_warn(WARN_OTHER, "pre-defining non ID `%s\'\n", definition);

    l = nasm_malloc(sizeof(Line));
    l->next = predef;
    l->first = def;
    l->finishes = NULL;
    predef = l;
}


static char *pp_getline(void)
{
    char *line = NULL;
    Token *tline;

    while (true) {
        tline = pp_tokline();
        if (tline == &tok_pop) {
            /*
             * We popped the macro/include stack. If istk is empty,
             * we are at end of input, otherwise just loop back.
             */
            if (!istk)
                break;
        } else {
            /*
             * De-tokenize the line and emit it.
             */
            line = detoken(tline, true);
            free_tlist(tline);
            break;
        }
    }

    if (list_option('e') && istk && !istk->nolist && line && line[0]) {
        char *buf = nasm_strcat(" ;;; ", line);
        lfmt->line(LIST_MACRO, -1, buf);
        nasm_free(buf);
    }

    return line;
}


static void pp_include_path(struct strlist *list)
{
    ipath_list = list;
}


static Token *quote_any_token(Token *t)
{
    size_t len = t->len;
    char *p;

    p = nasm_quote(tok_text(t), &len);
    t->type = TOK_STRING;
    return set_text_free(t, p, len);
}


static inline Token *skip_white(Token *x)
{
    while (tok_white(x))
        x = x->next;

    return x;
}


static Token *zap_white(Token *x)
{
    while (tok_white(x))
        x = delete_Token(x);

    return x;
}


static inline char *tok_text_buf(struct Token *t)
{
    return (t->len <= INLINE_TEXT) ? t->text.a : t->text.p.ptr;
}


static bool parse_mmacro_spec(Token *tline, MMacro *def, const char *directive)
{
    tline = tline->next;
    tline = skip_white(tline);
    tline = expand_id(tline);
    if (!tok_type(tline, TOK_ID)) {
        nasm_nonfatal("`%s' expects a macro name", directive);
        return false;
    }

#if 0
    def->prev = NULL;
#endif
    def->name = dup_text(tline);
    def->plus = false;
    def->nolist = 0;
    def->nparam_min = 0;
    def->nparam_max = 0;

    tline = expand_smacro(tline->next);
    tline = skip_white(tline);
    if (!tok_type(tline, TOK_NUMBER))
        nasm_nonfatal("`%s' expects a parameter count", directive);
    else
        def->nparam_min = def->nparam_max = read_param_count(tok_text(tline));
    if (tline && tok_is(tline->next, '-')) {
        tline = tline->next->next;
        if (tok_is(tline, '*')) {
            def->nparam_max = INT_MAX;
        } else if (!tok_type(tline, TOK_NUMBER)) {
            nasm_nonfatal("`%s' expects a parameter count after `-'", directive);
        } else {
            def->nparam_max = read_param_count(tok_text(tline));
            if (def->nparam_min > def->nparam_max) {
                nasm_nonfatal("minimum parameter count exceeds maximum");
                def->nparam_max = def->nparam_min;
            }
        }
    }
    if (tline && tok_is(tline->next, '+')) {
        tline = tline->next;
        def->plus = true;
    }
    if (tline && tok_type(tline->next, TOK_ID) &&
	tline->next->len == 7 &&
        !nasm_stricmp(tline->next->text.a, ".nolist")) {
        tline = tline->next;
        if (!list_option('f'))
            def->nolist |= NL_LIST|NL_LINE;
    }

    /*
     * Handle default parameters.
     */
    def->ndefs = 0;
    if (tline && tline->next) {
        Token **comma;
        def->dlist = tline->next;
        tline->next = NULL;
        comma = count_mmac_params(def->dlist, &def->ndefs, &def->defaults);
        if (!ppopt.sane_empty_expansion && comma) {
            *comma = NULL;
            def->ndefs--;
            nasm_warn(WARN_MACRO_PARAMS_LEGACY,
                      "dropping trailing empty default parameter in defintion of multi-line macro `%s'",
                      def->name);
        }
    } else {
        def->dlist = NULL;
        def->defaults = NULL;
    }
    def->expansion = NULL;

    if (def->defaults && def->ndefs > def->nparam_max - def->nparam_min &&
        !def->plus) {
        /*
         *!macro-defaults [on] macros with more default than optional parameters
         *!  warns when a macro has more default parameters than optional parameters.
         *!  See \k{mlmacdef} for why might want to disable this warning.
         */
        nasm_warn(WARN_MACRO_DEFAULTS,
                   "too many default macro parameters in macro `%s'", def->name);
    }

    return true;
}


static SMacro *define_smacro(const char *mname, bool casesense,
                             Token *expansion, SMacro *tmpl)
{
    SMacro *smac, **smhead;
    struct hash_table *smtbl;
    Context *ctx;
    bool defining_alias = false;
    int nparam = 0;
    bool defined;

    if (tmpl) {
        defining_alias = tmpl->alias;
        nparam = tmpl->nparam;
        if (nparam && !defining_alias)
            mark_smac_params(expansion, tmpl, 0);
    }

    ctx = get_ctx(mname, &mname);

    defined = smacro_defined(ctx, mname, nparam, &smac, casesense, true);

    if (defined) {
        if (smac->alias) {
            if (smac->in_progress) {
                nasm_nonfatal("macro alias loop");
                goto fail;
            }

            if (!defining_alias && !ppopt.noaliases) {
                /* It is an alias macro; follow the alias link */
                SMacro *s;

                smac->in_progress = true;
                s = define_smacro(tok_text(smac->expansion), casesense,
                                  expansion, tmpl);
                smac->in_progress = false;
                return s;
            }
        }

        if (casesense ^ smac->casesense) {
            /*
             *!macro-def-case-single [on] single-line macro defined both case sensitive and insensitive
             *!  warns when a single-line macro is defined both case
             *!  sensitive and case insensitive.
             *!  The new macro
             *!  definition will override (shadow) the original one,
             *!  although the original macro is not deleted, and will
             *!  be re-exposed if the new macro is deleted with
             *!  \c{%undef}, or, if the original macro is the case
             *!  insensitive one, the macro call is done with a
             *!  different case.
             */
            nasm_warn(WARN_MACRO_DEF_CASE_SINGLE, "case %ssensitive definition of macro `%s' will shadow %ssensitive macro `%s'",
                      casesense ? "" : "in",
                      mname,
                      smac->casesense ? "" : "in",
                      smac->name);
            defined = false;
        } else if ((!!nparam) ^ (!!smac->nparam)) {
            /*
             * Most recent versions of NASM considered this an error,
             * so promote this warning to error by default.
             *
             *!macro-def-param-single [err] single-line macro defined with and without parameters
             *!  warns if the same single-line macro is defined with and
             *!  without parameters.
             *!  The new macro
             *!  definition will override (shadow) the original one,
             *!  although the original macro is not deleted, and will
             *!  be re-exposed if the new macro is deleted with
             *!  \c{%undef}.
             */
            nasm_warn(WARN_MACRO_DEF_PARAM_SINGLE,
                      "macro `%s' defined both with and without parameters",
                      mname);
            defined = false;
        } else if (smac->nparam < nparam) {
            /*
             *!macro-def-greedy-single [on] single-line macro
             *!  definition shadows greedy macro warns when a
             *!  single-line macro is defined which would match a
             *!  previously existing greedy definition.  The new macro
             *!  definition will override (shadow) the original one,
             *!  although the original macro is not deleted, and will
             *!  be re-exposed if the new macro is deleted with
             *!  \c{%undef}, and will be invoked if called with a
             *!  parameter count that does not match the new definition.
             */
            nasm_warn(WARN_MACRO_DEF_GREEDY_SINGLE,
                      "defining macro `%s' shadows previous greedy definition",
                      mname);
            defined = false;
        }
    }

    if (defined) {
        /*
         * We're redefinining, so we have to take over an
         * existing SMacro structure. This means freeing
         * what was already in it, but not the structure itself.
         */
        clear_smacro(smac);
    } else {
        /* Create a new macro */
        smtbl  = ctx ? &ctx->localmac : &smacros;
        smhead = (SMacro **) hash_findi_add(smtbl, mname);
        nasm_new(smac);
        smac->next = *smhead;
        *smhead = smac;
    }

    smac->name      = nasm_strdup(mname);
    smac->casesense = casesense;
    smac->expansion = expansion;
    smac->expand    = smacro_expand_default;
    if (tmpl) {
        smac->nparam     = tmpl->nparam;
        smac->params     = tmpl->params;
        smac->alias      = tmpl->alias;
        smac->greedy     = tmpl->greedy;
        if (tmpl->expand)
            smac->expand = tmpl->expand;
    }
    if (list_option('s')) {
        list_smacro_def((smac->alias ? PP_DEFALIAS : PP_DEFINE)
                        + !casesense, ctx, smac);
    }
    return smac;

fail:
    free_tlist(expansion);
    if (tmpl)
        free_smacro_members(tmpl);
    return NULL;
}


static Token *tokenize(const char *line)
{
    enum pp_token_type type;
    Token *list = NULL;
    Token *t, **tail = &list;

    while (*line) {
        const char *p = line;
        const char *ep = NULL;  /* End of token, for trimming the end */
        size_t toklen;
        char firstchar = *p;    /* Can be used to override the first char */

        if (*p == '%') {
            /*
             * Preprocessor construct; find the end of the token.
             * Classification is handled later, because %{...} can be
             * used to create any preprocessor token.
             */
            p++;
            if (*p == '+' && !nasm_isdigit(p[1])) {
                /* Paste token */
                p++;
            } else if (nasm_isdigit(*p) ||
                       ((*p == '-' || *p == '+') && nasm_isdigit(p[1]))) {
                do {
                    p++;
                }
                while (nasm_isdigit(*p));
            } else if (*p == '{' || *p == '[') {
                /* %{...} or %[...] */
                char firstchar = *p;
                char endchar = *p + 2; /* } or ] */
                int lvl = 1;
                line += (*p++ == '{'); /* Skip { but not [ (yet) */
                while (lvl) {
                    if (*p == firstchar) {
                        lvl++;
                    } else if (*p == endchar) {
                        lvl--;
                    } else if (nasm_isquote(*p)) {
                        p = nasm_skip_string(p);
                    }

                    /*
                     * *p can have been advanced to a null character by
                     * nasm_skip_string()
                     */
                    if (!*p) {
                        nasm_warn(WARN_OTHER, "unterminated %%%c construct",
                                  firstchar);
                        break;
                    }
                    p++;
                }
                ep = lvl ? p : p-1; /* Terminal character not part of token */
            } else if (*p == '?') {
                /* %? or %?? */
                p++;
                if (*p == '?')
                    p++;
            } else if (*p == '*' && p[1] == '?') {
                /* %*? and %*?? */
                p += 2;
                if (*p == '?')
                    p++;
            } else if (*p == '!') {
                /* Environment variable reference */
                p++;
                if (nasm_isidchar(*p)) {
                    do {
                        p++;
                    }
                    while (nasm_isidchar(*p));
                } else if (nasm_isquote(*p)) {
                    p = nasm_skip_string(p);
                    if (*p)
                        p++;
                    else
                        nasm_nonfatalf(ERR_PASS1, "unterminated %%! string");
                } else {
                    /* %! without anything else... */
                }
            } else if (*p == ',') {
                /* Conditional comma */
                p++;
            } else if (nasm_isidchar(*p) ||
                       ((*p == '%' || *p == '$') && nasm_isidchar(p[1]))) {
                /* Identifier or some sort */
                do {
                    p++;
                }
                while (nasm_isidchar(*p));
            } else if (*p == '%') {
                /* %% operator */
                p++;
            }

            if (!ep)
                ep = p;
            toklen = ep - line;

            /* Classify here, to handle %{...} correctly */
            if (toklen < 2) {
                type = TOK_OTHER;   /* % operator */
            } else {
                char c0 = line[1];

                switch (c0) {
                case '+':
                    type = (toklen == 2) ? TOK_PASTE : TOK_MMACRO_PARAM;
                    break;

                case '-':
                    type = TOK_MMACRO_PARAM;
                    break;

                case '?':
                    if (toklen == 2)
                        type = TOK_PREPROC_Q;
                    else if (toklen == 3 && line[2] == '?')
                        type = TOK_PREPROC_QQ;
                    else
                        type = TOK_PREPROC_ID;
                    break;

                case '*':
                    type = TOK_OTHER;
                    if (line[2] == '?') {
                        if (toklen == 3)
                            type = TOK_PREPROC_SQ;
                        else if (toklen == 4 && line[3] == '?')
                            type = TOK_PREPROC_SQQ;
                    }
                    break;

                case '!':
                    type = (toklen == 2) ? TOK_OTHER : TOK_ENVIRON;
                    break;

                case '%':
                    type = (toklen == 2) ? TOK_OTHER : TOK_LOCAL_SYMBOL;
                    break;

                case '$':
                    type = (toklen == 2) ? TOK_OTHER : TOK_LOCAL_MACRO;
                    break;

                case '[':
                    line += 2;  /* Skip %[ */
                    firstchar = *line; /* Don't clobber */
                    toklen -= 2;
                    type = TOK_INDIRECT;
                    break;

                case ',':
                    type = (toklen == 2) ? TOK_COND_COMMA : TOK_PREPROC_ID;
                    break;

                case '\'':
                case '\"':
                case '`':
                    /* %{'string'} */
                    type = TOK_PREPROC_ID;
                    break;

                case ':':
                    type = TOK_MMACRO_PARAM; /* %{:..} */
                    break;

                default:
                    if (nasm_isdigit(c0))
                        type = TOK_MMACRO_PARAM;
                    else if (nasm_isidchar(c0) || toklen > 2)
                        type = TOK_PREPROC_ID;
                    else
                        type = TOK_OTHER;
                    break;
                }
            }
        } else if (nasm_isidstart(*p) || (*p == '$' && nasm_isidstart(p[1]))) {
            /*
             * An identifier. This includes the ? operator, which is
             * treated as a keyword, not as a special character
             * operator
             */
            type = TOK_ID;
            while (nasm_isidchar(*++p))
                ;
         } else if (nasm_isquote(*p)) {
            /*
             * A string token.
             */
            type = TOK_STRING;
            p = nasm_skip_string(p);

            if (*p) {
                p++;
            } else {
                nasm_warn(WARN_OTHER, "unterminated string");
                /* Handling unterminated strings by UNV */
                /* type = -1; */
            }
        } else if (p[0] == '$' && p[1] == '$') {
            /* TOKEN_BASE - treat as TOK_ID for pasting purposes */
            type = TOK_ID;
            p += 2;
        } else if (nasm_isnumstart(*p)) {
            bool is_hex = false;
            bool is_float = false;
            bool has_e = false;
            char c;

            /*
             * A numeric token.
             */

            if (*p == '$') {
                p++;
                is_hex = true;
            }

            for (;;) {
                c = *p++;

                if (!is_hex && (c == 'e' || c == 'E')) {
                    has_e = true;
                    if (*p == '+' || *p == '-') {
                        /*
                         * e can only be followed by +/- if it is either a
                         * prefixed hex number or a floating-point number
                         */
                        p++;
                        is_float = true;
                    }
                } else if (c == 'H' || c == 'h' || c == 'X' || c == 'x') {
                    is_hex = true;
                } else if (c == 'P' || c == 'p') {
                    is_float = true;
                    if (*p == '+' || *p == '-')
                        p++;
                } else if (nasm_isnumchar(c))
                    ; /* just advance */
                else if (c == '.') {
                    /*
                     * we need to deal with consequences of the legacy
                     * parser, like "1.nolist" being two tokens
                     * (TOK_NUMBER, TOK_ID) here; at least give it
                     * a shot for now.  In the future, we probably need
                     * a flex-based scanner with proper pattern matching
                     * to do it as well as it can be done.  Nothing in
                     * the world is going to help the person who wants
                     * 0x123.p16 interpreted as two tokens, though.
                     */
                    const char *r = p;
                    while (*r == '_')
                        r++;

                    if (nasm_isdigit(*r) || (is_hex && nasm_isxdigit(*r)) ||
                        (!is_hex && (*r == 'e' || *r == 'E')) ||
                        (*r == 'p' || *r == 'P')) {
                        p = r;
                        is_float = true;
                    } else
                        break;  /* Terminate the token */
                } else
                    break;
            }
            p--;        /* Point to first character beyond number */

            if (p == line+1 && *line == '$') {
                /* TOKEN_HERE - treat as TOK_ID for pasting purposes */
                type = TOK_ID;
            } else {
                if (has_e && !is_hex) {
                    /* 1e13 is floating-point, but 1e13h is not */
                    is_float = true;
                }

                type = is_float ? TOK_FLOAT : TOK_NUMBER;
            }
        } else if (nasm_isspace(*p)) {
            type = TOK_WHITESPACE;
            p = nasm_skip_spaces(p);
            /*
             * Whitespace just before end-of-line is discarded by
             * pretending it's a comment; whitespace just before a
             * comment gets lumped into the comment.
             */
            if (!*p || *p == ';') {
                type = TOK_COMMENT;
                while (*p)
                    p++;
            }
        } else if (*p == ';') {
            type = TOK_COMMENT;
            while (*p)
                p++;
        } else {
            /*
             * Anything else is an operator of some kind. We check
             * for all the double-character operators (>>, <<, //,
             * %%, <=, >=, ==, !=, <>, &&, ||, ^^) and the triple-
	     * character operators (<<<, >>>, <=>) but anything
             * else is a single-character operator.
             */
            type = TOK_OTHER;
	    switch (*p++) {
	    case '>':
		if (*p == '>') {
		    p++;
		    if (*p == '>')
			p++;
		} else if (*p == '=') {
                    p++;
                }
		break;

	    case '<':
		if (*p == '<') {
		    p++;
		    if (*p == '<')
			p++;
		} else if (*p == '=') {
		    p++;
		    if (*p == '>')
			p++;
		} else if (*p == '>') {
		    p++;
		}
		break;

	    case '!':
		if (*p == '=')
		    p++;
		break;

	    case '/':
	    case '=':
	    case '&':
	    case '|':
	    case '^':
		/* These operators can be doubled but nothing else */
		if (*p == p[-1])
		    p++;
		break;

	    default:
		break;
	    }
        }

        if (type == TOK_WHITESPACE) {
            *tail = t = new_White(NULL);
            tail = &t->next;
        } else if (type != TOK_COMMENT) {
            if (!ep)
                ep = p;
            *tail = t = new_Token(NULL, type, line, ep - line);
            *tok_text_buf(t) = firstchar; /* E.g. %{foo} -> {foo -> %foo */
            tail = &t->next;
        }
        line = p;
    }
    return list;
}


static Token *delete_Token(Token *t)
{
    Token *next = t->next;
    nasm_free(t);
    return next;
}


static void process_size_override(insn *result, operand *op)
{
    if (tasm_compatible_mode) {
        switch (tokval.t_integer) {
            /* For TASM compatibility a size override inside the
             * brackets changes the size of the operand, not the
             * address type of the operand as it does in standard
             * NASM syntax. Hence:
             *
             *  mov     eax,[DWORD val]
             *
             * is valid syntax in TASM compatibility mode. Note that
             * you lose the ability to override the default address
             * type for the instruction, but we never use anything
             * but 32-bit flat model addressing in our code.
             */
        case S_BYTE:
            op->type |= BITS8;
            break;
        case S_WORD:
            op->type |= BITS16;
            break;
        case S_DWORD:
        case S_LONG:
            op->type |= BITS32;
            break;
        case S_QWORD:
            op->type |= BITS64;
            break;
        case S_TWORD:
            op->type |= BITS80;
            break;
        case S_OWORD:
            op->type |= BITS128;
            break;
        default:
            nasm_nonfatal("invalid operand size specification");
            break;
        }
    } else {
        /* Standard NASM compatible syntax */
        switch (tokval.t_integer) {
        case S_NOSPLIT:
            op->eaflags |= EAF_TIMESTWO;
            break;
        case S_REL:
            op->eaflags |= EAF_REL;
            break;
        case S_ABS:
            op->eaflags |= EAF_ABS;
            break;
        case S_BYTE:
            op->disp_size = 8;
            op->eaflags |= EAF_BYTEOFFS;
            break;
        case P_A16:
        case P_A32:
        case P_A64:
            if (result->prefixes[PPS_ASIZE] &&
                result->prefixes[PPS_ASIZE] != tokval.t_integer)
                nasm_nonfatal("conflicting address size specifications");
            else
                result->prefixes[PPS_ASIZE] = tokval.t_integer;
            break;
        case S_WORD:
            op->disp_size = 16;
            op->eaflags |= EAF_WORDOFFS;
            break;
        case S_DWORD:
        case S_LONG:
            op->disp_size = 32;
            op->eaflags |= EAF_WORDOFFS;
            break;
        case S_QWORD:
            op->disp_size = 64;
            op->eaflags |= EAF_WORDOFFS;
            break;
        default:
            nasm_nonfatal("invalid size specification in"
                          " effective address");
            break;
        }
    }
}


_register_extend( oid *base, size_t len )
{
    extend_registration_block         *eptr;
    oid oid_buf[MAX_OID_LEN];

    netsnmp_table_data                *dinfo;
    netsnmp_table_registration_info   *tinfo;
    netsnmp_watcher_info              *winfo;
    netsnmp_handler_registration      *reg = NULL;
    int                                rc;

    for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
        if (!snmp_oid_compare( base, len, eptr->root_oid, eptr->oid_len))
            return eptr;
    }
    if (!eptr) {
        eptr = SNMP_MALLOC_TYPEDEF( extend_registration_block );
        if (!eptr)
            return NULL;
        eptr->root_oid = snmp_duplicate_objid( base, len );
        eptr->oid_len  = len;
        eptr->num_entries = 0;
        eptr->ehead       = NULL;
        eptr->dinfo       = netsnmp_create_table_data( "nsExtendTable" );
        eptr->next        = ereg_head;
        ereg_head         = eptr;
    }

    dinfo = eptr->dinfo;
    memcpy( oid_buf, base, len*sizeof(oid) );

        /*
         * Register the configuration table
         */
    tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
    netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
    tinfo->min_column = COLUMN_EXTCFG_FIRST_COLUMN;
    tinfo->max_column = COLUMN_EXTCFG_LAST_COLUMN;
    oid_buf[len] = 2;
#ifndef NETSNMP_NO_WRITE_SUPPORT
    reg   = netsnmp_create_handler_registration(
                "nsExtendConfigTable", handle_nsExtendConfigTable,
                oid_buf, len+1, HANDLER_CAN_RWRITE);
#else /* !NETSNMP_NO_WRITE_SUPPORT */
    reg   = netsnmp_create_handler_registration(
                "nsExtendConfigTable", handle_nsExtendConfigTable,
                oid_buf, len+1, HANDLER_CAN_RONLY);
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
    rc = netsnmp_register_table_data( reg, dinfo, tinfo );
    if (rc != SNMPERR_SUCCESS) {
        goto bail;
    }
    netsnmp_handler_owns_table_info(reg->handler->next);
    eptr->reg[0] = reg;

        /*
         * Register the main output table
         *   using the same table_data handle.
         * This is sufficient to link the two tables,
         *   and implement the AUGMENTS behaviour
         */
    tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
    netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
    tinfo->min_column = COLUMN_EXTOUT1_FIRST_COLUMN;
    tinfo->max_column = COLUMN_EXTOUT1_LAST_COLUMN;
    oid_buf[len] = 3;
    reg   = netsnmp_create_handler_registration(
                "nsExtendOut1Table", handle_nsExtendOutput1Table,
                oid_buf, len+1, HANDLER_CAN_RONLY);
    rc = netsnmp_register_table_data( reg, dinfo, tinfo );
    if (rc != SNMPERR_SUCCESS)
        goto bail;
    netsnmp_handler_owns_table_info(reg->handler->next);
    eptr->reg[1] = reg;

        /*
         * Register the multi-line output table
         *   using a simple table helper.
         * This handles extracting the indexes from
         *   the request OID, but leaves most of
         *   the work to our handler routine.
         * Still, it was nice while it lasted...
         */
    tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
    netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, ASN_INTEGER, 0 );
    tinfo->min_column = COLUMN_EXTOUT2_FIRST_COLUMN;
    tinfo->max_column = COLUMN_EXTOUT2_LAST_COLUMN;
    oid_buf[len] = 4;
    reg   = netsnmp_create_handler_registration(
                "nsExtendOut2Table", handle_nsExtendOutput2Table,
                oid_buf, len+1, HANDLER_CAN_RONLY);
    rc = netsnmp_register_table( reg, tinfo );
    if (rc != SNMPERR_SUCCESS)
        goto bail;
    netsnmp_handler_owns_table_info(reg->handler->next);
    eptr->reg[2] = reg;

        /*
         * Register a watched scalar to keep track of the number of entries
         */
    oid_buf[len] = 1;
    reg   = netsnmp_create_handler_registration(
                "nsExtendNumEntries", NULL,
                oid_buf, len+1, HANDLER_CAN_RONLY);
    winfo = netsnmp_create_watcher_info(
                &(eptr->num_entries), sizeof(eptr->num_entries),
                ASN_INTEGER, WATCHER_FIXED_SIZE);
    rc = netsnmp_register_watched_scalar2( reg, winfo );
    if (rc != SNMPERR_SUCCESS)
        goto bail;
    eptr->reg[3] = reg;

    return eptr;

bail:
    if (eptr->reg[3])
        netsnmp_unregister_handler(eptr->reg[3]);
    if (eptr->reg[2])
        netsnmp_unregister_handler(eptr->reg[2]);
    if (eptr->reg[1])
        netsnmp_unregister_handler(eptr->reg[1]);
    if (eptr->reg[0])
        netsnmp_unregister_handler(eptr->reg[0]);
    return NULL;
}


handle_nsExtendOutput1Table(netsnmp_mib_handler          *handler,
                     netsnmp_handler_registration *reginfo,
                     netsnmp_agent_request_info   *reqinfo,
                     netsnmp_request_info         *requests)
{
    netsnmp_request_info       *request;
    netsnmp_table_request_info *table_info;
    netsnmp_extend             *extension;
    int len;

    for ( request=requests; request; request=request->next ) {
        if (request->processed)
            continue;
        table_info = netsnmp_extract_table_info( request );
        extension  = (netsnmp_extend*)netsnmp_extract_table_row_data( request );

        DEBUGMSGTL(( "nsExtendTable:output1", "varbind: "));
        DEBUGMSGOID(("nsExtendTable:output1", request->requestvb->name,
                                              request->requestvb->name_length));
        DEBUGMSG((   "nsExtendTable:output1", "\n"));

        switch (reqinfo->mode) {
        case MODE_GET:
            if (!extension || !(extension->flags & NS_EXTEND_FLAGS_ACTIVE)) {
                /*
                 * If this row is inactive, then skip it.
                 */
                netsnmp_set_request_error(reqinfo, request,
                                          SNMP_NOSUCHINSTANCE);
                continue;
            }
            if (!(extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
                (netsnmp_cache_check_and_reload( extension->cache ) < 0 )) {
                /*
                 * If reloading the output cache of a 'run-on-read'
                 * entry fails, then skip it.
                 */
                netsnmp_set_request_error(reqinfo, request,
                                          SNMP_NOSUCHINSTANCE);
                continue;
            }
            if ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
                (netsnmp_cache_check_expired( extension->cache ) == 1 )) {
                /*
                 * If the output cache of a 'run-on-write'
                 * entry has expired, then skip it.
                 */
                netsnmp_set_request_error(reqinfo, request,
                                          SNMP_NOSUCHINSTANCE);
                continue;
            }

            switch (table_info->colnum) {
            case COLUMN_EXTOUT1_OUTLEN:
                snmp_set_var_typed_value(
                     request->requestvb, ASN_INTEGER,
                    (u_char*)&extension->out_len, sizeof(int));
                break;
            case COLUMN_EXTOUT1_OUTPUT1:
                /*
                 * If we've got more than one line,
                 * find the length of the first one.
                 * Otherwise find the length of the whole string.
                 */
                if (extension->numlines > 1) {
                    len = (extension->lines[1])-(extension->output) -1;
                } else if (extension->output) {
                    len = strlen(extension->output);
                } else {
                    len = 0;
                }
                snmp_set_var_typed_value(
                     request->requestvb, ASN_OCTET_STR,
                     extension->output, len);
                break;
            case COLUMN_EXTOUT1_OUTPUT2:
                snmp_set_var_typed_value(
                     request->requestvb, ASN_OCTET_STR,
                     extension->output,
                    (extension->output)?extension->out_len:0);
                break;
            case COLUMN_EXTOUT1_NUMLINES:
                snmp_set_var_typed_value(
                     request->requestvb, ASN_INTEGER,
                    (u_char*)&extension->numlines, sizeof(int));
                break;
            case COLUMN_EXTOUT1_RESULT:
                snmp_set_var_typed_value(
                     request->requestvb, ASN_INTEGER,
                    (u_char*)&extension->result, sizeof(int));
                break;
            default:
                netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
                continue;
            }
            break;
        default:
            netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
            return SNMP_ERR_GENERR;
        }
    }
    return SNMP_ERR_NOERROR;
}


fixExec2Error(int action,
             u_char * var_val,
             u_char var_val_type,
             size_t var_val_len,
             u_char * statP, oid * name, size_t name_len)
{
    netsnmp_old_extend *exten = NULL;
    unsigned int idx;

    idx = name[name_len-1] -1;
    exten = &compatability_entries[ idx ];

#if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
    switch (action) {
    case MODE_SET_RESERVE1:
        if (var_val_type != ASN_INTEGER) {
            snmp_log(LOG_ERR, "Wrong type != int\n");
            return SNMP_ERR_WRONGTYPE;
        }
        idx = *((long *) var_val);
        if (idx != 1) {
            snmp_log(LOG_ERR, "Wrong value != 1\n");
            return SNMP_ERR_WRONGVALUE;
        }
        if (!exten || !exten->efix_entry) {
            snmp_log(LOG_ERR, "No command to run\n");
            return SNMP_ERR_GENERR;
        }
        return SNMP_ERR_NOERROR;

    case MODE_SET_COMMIT:
        netsnmp_cache_check_and_reload( exten->efix_entry->cache );
    }
#endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */
    return SNMP_ERR_NOERROR;
}


_extend_find_entry( netsnmp_request_info       *request,
                    netsnmp_table_request_info *table_info,
                    int mode  )
{
    netsnmp_extend            *eptr;
    extend_registration_block *ereg;
    unsigned int line_idx;
    oid oid_buf[MAX_OID_LEN];
    int oid_len;
    int i;
    char  *token;
    size_t token_len;

    if (!request || !table_info || !table_info->indexes
                 || !table_info->indexes->next_variable) {
        DEBUGMSGTL(( "nsExtendTable:output2", "invalid invocation\n"));
        return NULL;
    }

    ereg = _find_extension_block( request->requestvb->name,
                                  request->requestvb->name_length );

    /***
     *  GET handling - find the exact entry being requested
     ***/
    if ( mode == MODE_GET ) {
        DEBUGMSGTL(( "nsExtendTable:output2", "GET: %s / %ld\n ",
                      table_info->indexes->val.string,
                     *table_info->indexes->next_variable->val.integer));
        for ( eptr = ereg->ehead; eptr; eptr = eptr->next ) {
            if ( !strcmp( eptr->token, (char *) table_info->indexes->val.string ))
                break;
        }

        if ( eptr ) {
            /*
             * Ensure the output is available...
             */
            if (!(eptr->flags & NS_EXTEND_FLAGS_ACTIVE) ||
               (netsnmp_cache_check_and_reload( eptr->cache ) < 0 ))
                return NULL;

            /*
             * ...and check the line requested is valid
             */
            line_idx = *table_info->indexes->next_variable->val.integer;
            if (line_idx < 1 || line_idx > eptr->numlines)
                return NULL;
        }
    }

        /***
         *  GETNEXT handling - find the first suitable entry
         ***/
    else {
        if (!table_info->indexes->val_len ) {
            DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: first entry\n"));
            /*
             * Beginning of the table - find the first active
             *  (and successful) entry, and use the first line of it
             */
            for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
                if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
                    (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
                    line_idx = 1;
                    break;
                }
            }
        } else {
            token     =  (char *) table_info->indexes->val.string;
            token_len =  table_info->indexes->val_len;
            line_idx  = *table_info->indexes->next_variable->val.integer;
            DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: %s / %d\n ",
                          token, line_idx ));
            /*
             * Otherwise, find the first entry not earlier
             * than the requested token...
             */
            for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
                if ( strlen(eptr->token) > token_len )
                    break;
                if ( strlen(eptr->token) == token_len &&
                     strcmp(eptr->token, token) >= 0 )
                    break;
            }
            if (!eptr)
                return NULL;    /* (assuming there is one) */

            /*
             * ... and make sure it's active & the output is available
             * (or use the first following entry that is)
             */
            for (    ; eptr; eptr = eptr->next ) {
                if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
                    (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
                    break;
                }
                line_idx = 1;
            }

            if (!eptr)
                return NULL;    /* (assuming there is one) */

            /*
             *  If we're working with the same entry that was requested,
             *  see whether we've reached the end of the output...
             */
            if (!strcmp( eptr->token, token )) {
                if ( eptr->numlines <= line_idx ) {
                    /*
                     * ... and if so, move on to the first line
                     * of the next (active and successful) entry.
                     */
                    line_idx = 1;
                    for (eptr = eptr->next ; eptr; eptr = eptr->next ) {
                        if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
                            (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
                            break;
                        }
                    }
                } else {
                    /*
                     * Otherwise just use the next line of this entry.
                     */
                    line_idx++;
                }
            }
            else {
                /*
                 * If this is not the same entry that was requested,
                 * then we should return the first line.
                 */
                line_idx = 1;
            }
        }
        if (eptr) {
            DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT -> %s / %d\n ",
                          eptr->token, line_idx));
            /*
             * Since we're processing a GETNEXT request,
             * now we've found the appropriate entry (and line),
             * we need to update the varbind OID ...
             */
            memset(oid_buf, 0, sizeof(oid_buf));
            oid_len = ereg->oid_len;
            memcpy( oid_buf, ereg->root_oid, oid_len*sizeof(oid));
            oid_buf[ oid_len++ ] = 4;    /* nsExtendOutput2Table */
            oid_buf[ oid_len++ ] = 1;    /* nsExtendOutput2Entry */
            oid_buf[ oid_len++ ] = COLUMN_EXTOUT2_OUTLINE;
                                         /* string token index */
            oid_buf[ oid_len++ ] = strlen(eptr->token);
            for ( i=0; i < (int)strlen(eptr->token); i++ )
                oid_buf[ oid_len+i ] = eptr->token[i];
            oid_len += strlen( eptr->token );
                                         /* plus line number */
            oid_buf[ oid_len++ ] = line_idx;
            snmp_set_var_objid( request->requestvb, oid_buf, oid_len );
            /*
             * ... and index values to match.
             */
            snmp_set_var_value( table_info->indexes,
                                eptr->token, strlen(eptr->token));
            snmp_set_var_value( table_info->indexes->next_variable,
                                (const u_char*)&line_idx, sizeof(line_idx));
        }
    }
    return eptr;  /* Finally, signal success */
}


futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
		  int flags, u64 range_ns)
{
	if (!time)
		return NULL;

	hrtimer_init_sleeper_on_stack(timeout, (flags & FLAGS_CLOCKRT) ?
				      CLOCK_REALTIME : CLOCK_MONOTONIC,
				      HRTIMER_MODE_ABS);
	/*
	 * If range_ns is 0, calling hrtimer_set_expires_range_ns() is
	 * effectively the same as calling hrtimer_set_expires().
	 */
	hrtimer_set_expires_range_ns(&timeout->timer, *time, range_ns);

	return timeout;
}


struct timespec64 current_time(struct inode *inode)
{
	struct timespec64 now;

	ktime_get_coarse_real_ts64(&now);

	if (unlikely(!inode->i_sb)) {
		WARN(1, "current_time() called with uninitialized super_block in the inode");
		return now;
	}

	return timestamp_truncate(now, inode);
}


static struct inode *alloc_inode(struct super_block *sb)
{
	const struct super_operations *ops = sb->s_op;
	struct inode *inode;

	if (ops->alloc_inode)
		inode = ops->alloc_inode(sb);
	else
		inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);

	if (!inode)
		return NULL;

	if (unlikely(inode_init_always(sb, inode))) {
		if (ops->destroy_inode) {
			ops->destroy_inode(inode);
			if (!ops->free_inode)
				return NULL;
		}
		inode->free_inode = ops->free_inode;
		i_callback(&inode->i_rcu);
		return NULL;
	}

	return inode;
}


struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval,
		int (*test)(struct inode *, void *), void *data)
{
	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
	struct inode *inode;

	spin_lock(&inode_hash_lock);
	inode = find_inode(sb, head, test, data);
	spin_unlock(&inode_hash_lock);

	return IS_ERR(inode) ? NULL : inode;
}


int file_modified(struct file *file)
{
	int err;

	/*
	 * Clear the security bits if the process is not being run by root.
	 * This keeps people from modifying setuid and setgid binaries.
	 */
	err = file_remove_privs(file);
	if (err)
		return err;

	if (unlikely(file->f_mode & FMODE_NOCMTIME))
		return 0;

	return file_update_time(file);
}


int inode_init_always(struct super_block *sb, struct inode *inode)
{
	static const struct inode_operations empty_iops;
	static const struct file_operations no_open_fops = {.open = no_open};
	struct address_space *const mapping = &inode->i_data;

	inode->i_sb = sb;
	inode->i_blkbits = sb->s_blocksize_bits;
	inode->i_flags = 0;
	atomic64_set(&inode->i_sequence, 0);
	atomic_set(&inode->i_count, 1);
	inode->i_op = &empty_iops;
	inode->i_fop = &no_open_fops;
	inode->__i_nlink = 1;
	inode->i_opflags = 0;
	if (sb->s_xattr)
		inode->i_opflags |= IOP_XATTR;
	i_uid_write(inode, 0);
	i_gid_write(inode, 0);
	atomic_set(&inode->i_writecount, 0);
	inode->i_size = 0;
	inode->i_write_hint = WRITE_LIFE_NOT_SET;
	inode->i_blocks = 0;
	inode->i_bytes = 0;
	inode->i_generation = 0;
	inode->i_pipe = NULL;
	inode->i_bdev = NULL;
	inode->i_cdev = NULL;
	inode->i_link = NULL;
	inode->i_dir_seq = 0;
	inode->i_rdev = 0;
	inode->dirtied_when = 0;

#ifdef CONFIG_CGROUP_WRITEBACK
	inode->i_wb_frn_winner = 0;
	inode->i_wb_frn_avg_time = 0;
	inode->i_wb_frn_history = 0;
#endif

	if (security_inode_alloc(inode))
		goto out;
	spin_lock_init(&inode->i_lock);
	lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);

	init_rwsem(&inode->i_rwsem);
	lockdep_set_class(&inode->i_rwsem, &sb->s_type->i_mutex_key);

	atomic_set(&inode->i_dio_count, 0);

	mapping->a_ops = &empty_aops;
	mapping->host = inode;
	mapping->flags = 0;
	mapping->wb_err = 0;
	atomic_set(&mapping->i_mmap_writable, 0);
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
	atomic_set(&mapping->nr_thps, 0);
#endif
	mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE);
	mapping->private_data = NULL;
	mapping->writeback_index = 0;
	inode->i_private = NULL;
	inode->i_mapping = mapping;
	INIT_HLIST_HEAD(&inode->i_dentry);	/* buggered by rcu freeing */
#ifdef CONFIG_FS_POSIX_ACL
	inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
#endif

#ifdef CONFIG_FSNOTIFY
	inode->i_fsnotify_mask = 0;
#endif
	inode->i_flctx = NULL;
	this_cpu_inc(nr_inodes);

	return 0;
out:
	return -ENOMEM;
}


SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
		struct __kernel_timespec __user *, utime, u32 __user *, uaddr2,
		u32, val3)
{
	struct timespec64 ts;
	ktime_t t, *tp = NULL;
	u32 val2 = 0;
	int cmd = op & FUTEX_CMD_MASK;

	if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
		      cmd == FUTEX_WAIT_BITSET ||
		      cmd == FUTEX_WAIT_REQUEUE_PI)) {
		if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))
			return -EFAULT;
		if (get_timespec64(&ts, utime))
			return -EFAULT;
		if (!timespec64_valid(&ts))
			return -EINVAL;

		t = timespec64_to_ktime(ts);
		if (cmd == FUTEX_WAIT)
			t = ktime_add_safe(ktime_get(), t);
		tp = &t;
	}
	/*
	 * requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.
	 * number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.
	 */
	if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
	    cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
		val2 = (u32) (unsigned long) utime;

	return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
}


static void __user *futex_uaddr(struct robust_list __user *entry,
				compat_long_t futex_offset)
{
	compat_uptr_t base = ptr_to_compat(entry);
	void __user *uaddr = compat_ptr(base + futex_offset);

	return uaddr;
}


static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
		      ktime_t *abs_time, u32 bitset)
{
	struct hrtimer_sleeper timeout, *to;
	struct restart_block *restart;
	struct futex_hash_bucket *hb;
	struct futex_q q = futex_q_init;
	int ret;

	if (!bitset)
		return -EINVAL;
	q.bitset = bitset;

	to = futex_setup_timer(abs_time, &timeout, flags,
			       current->timer_slack_ns);
retry:
	/*
	 * Prepare to wait on uaddr. On success, holds hb lock and increments
	 * q.key refs.
	 */
	ret = futex_wait_setup(uaddr, val, flags, &q, &hb);
	if (ret)
		goto out;

	/* queue_me and wait for wakeup, timeout, or a signal. */
	futex_wait_queue_me(hb, &q, to);

	/* If we were woken (and unqueued), we succeeded, whatever. */
	ret = 0;
	/* unqueue_me() drops q.key ref */
	if (!unqueue_me(&q))
		goto out;
	ret = -ETIMEDOUT;
	if (to && !to->task)
		goto out;

	/*
	 * We expect signal_pending(current), but we might be the
	 * victim of a spurious wakeup as well.
	 */
	if (!signal_pending(current))
		goto retry;

	ret = -ERESTARTSYS;
	if (!abs_time)
		goto out;

	restart = &current->restart_block;
	restart->fn = futex_wait_restart;
	restart->futex.uaddr = uaddr;
	restart->futex.val = val;
	restart->futex.time = *abs_time;
	restart->futex.bitset = bitset;
	restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;

	ret = -ERESTART_RESTARTBLOCK;

out:
	if (to) {
		hrtimer_cancel(&to->timer);
		destroy_hrtimer_on_stack(&to->timer);
	}
	return ret;
}


struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode)
{
	struct super_block *sb = inode->i_sb;
	unsigned int gran = sb->s_time_gran;

	t.tv_sec = clamp(t.tv_sec, sb->s_time_min, sb->s_time_max);
	if (unlikely(t.tv_sec == sb->s_time_max || t.tv_sec == sb->s_time_min))
		t.tv_nsec = 0;

	/* Avoid division in the common cases 1 ns and 1 s. */
	if (gran == 1)
		; /* nothing */
	else if (gran == NSEC_PER_SEC)
		t.tv_nsec = 0;
	else if (gran > 1 && gran < NSEC_PER_SEC)
		t.tv_nsec -= t.tv_nsec % gran;
	else
		WARN(1, "invalid file time granularity: %u", gran);
	return t;
}


static int reqs_reencrypt_online(uint32_t reqs)
{
	return reqs & CRYPT_REQUIREMENT_ONLINE_REENCRYPT;
}


int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr *hdr, struct crypt_dm_active_device *dmd, uint32_t flags)
{
	int r, ret;
	struct dm_target *tgt;
	crypt_status_info ci;
	struct crypt_dm_active_device dmdc;
	char **dep, deps_uuid_prefix[40], *deps[MAX_DM_DEPS+1] = { 0 };
	const char *namei = NULL;
	struct crypt_lock_handle *reencrypt_lock = NULL;

	if (!dmd || !dmd->uuid || strncmp(CRYPT_LUKS2, dmd->uuid, sizeof(CRYPT_LUKS2)-1))
		return -EINVAL;

	/* uuid mismatch with metadata (if available) */
	if (hdr && crypt_uuid_cmp(dmd->uuid, hdr->uuid))
		return -EINVAL;

	r = snprintf(deps_uuid_prefix, sizeof(deps_uuid_prefix), CRYPT_SUBDEV "-%.32s", dmd->uuid + 6);
	if (r < 0 || (size_t)r != (sizeof(deps_uuid_prefix) - 1))
		return -EINVAL;

	tgt = &dmd->segment;

	/* TODO: We have LUKS2 dependencies now */
	if (hdr && single_segment(dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd))
		namei = device_dm_name(tgt->data_device);

	r = dm_device_deps(cd, name, deps_uuid_prefix, deps, ARRAY_SIZE(deps));
	if (r < 0)
		goto out;

	if (contains_reencryption_helper(deps)) {
		r = LUKS2_reencrypt_lock_by_dm_uuid(cd, dmd->uuid, &reencrypt_lock);
		if (r) {
			if (r == -EBUSY)
				log_err(cd, _("Reencryption in-progress. Cannot deactivate device."));
			else
				log_err(cd, _("Failed to get reencryption lock."));
			goto out;
		}
	}

	dep = deps;
	while (*dep) {
		if (is_reencryption_helper(*dep) && (dm_status_suspended(cd, *dep) > 0)) {
			if (dm_error_device(cd, *dep))
				log_err(cd, _("Failed to replace suspended device %s with dm-error target."), *dep);
		}
		dep++;
	}

	r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmdc);
	if (r < 0) {
		memset(&dmdc, 0, sizeof(dmdc));
		dmdc.segment.type = DM_UNKNOWN;
	}

	/* Remove top level device first */
	r = dm_remove_device(cd, name, flags);
	if (!r) {
		tgt = &dmdc.segment;
		while (tgt) {
			if (tgt->type == DM_CRYPT)
				crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
			tgt = tgt->next;
		}
	}
	dm_targets_free(cd, &dmdc);

	/* TODO: We have LUKS2 dependencies now */
	if (r >= 0 && namei) {
		log_dbg(cd, "Deactivating integrity device %s.", namei);
		r = dm_remove_device(cd, namei, 0);
	}

	if (!r) {
		ret = 0;
		dep = deps;
		while (*dep) {
			log_dbg(cd, "Deactivating LUKS2 dependent device %s.", *dep);
			r = dm_query_device(cd, *dep, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmdc);
			if (r < 0) {
				memset(&dmdc, 0, sizeof(dmdc));
				dmdc.segment.type = DM_UNKNOWN;
			}

			r = dm_remove_device(cd, *dep, flags);
			if (r < 0) {
				ci = crypt_status(cd, *dep);
				if (ci == CRYPT_BUSY)
					log_err(cd, _("Device %s is still in use."), *dep);
				if (ci == CRYPT_INACTIVE)
					r = 0;
			}
			if (!r) {
				tgt = &dmdc.segment;
				while (tgt) {
					if (tgt->type == DM_CRYPT)
						crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
					tgt = tgt->next;
				}
			}
			dm_targets_free(cd, &dmdc);
			if (r && !ret)
				ret = r;
			dep++;
		}
		r = ret;
	}

out:
	LUKS2_reencrypt_unlock(cd, reencrypt_lock);
	dep = deps;
	while (*dep)
		free(*dep++);

	return r;
}


int LUKS2_segments_count(struct luks2_hdr *hdr)
{
	if (!hdr)
		return -EINVAL;

	return json_segments_count(LUKS2_get_segments_jobj(hdr));
}


uint64_t LUKS2_hdr_and_areas_size_jobj(json_object *jobj)
{
	return 2 * LUKS2_metadata_size_jobj(jobj) + LUKS2_keyslots_size_jobj(jobj);
}


static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
{
	struct interval *intervals;
	json_object *jobj_keyslots, *jobj_offset, *jobj_length, *jobj_segments, *jobj_area;
	int length, ret, i = 0;
	uint64_t metadata_size;

	if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
		return 1;

	/* segments are already validated */
	if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments))
		return 1;

	/* config is already validated */
	metadata_size = LUKS2_metadata_size_jobj(hdr_jobj);

	length = json_object_object_length(jobj_keyslots);

	/* Empty section */
	if (length == 0)
		return 0;

	if (length < 0) {
		log_dbg(cd, "Invalid keyslot areas specification.");
		return 1;
	}

	intervals = malloc(length * sizeof(*intervals));
	if (!intervals) {
		log_dbg(cd, "Not enough memory.");
		return -ENOMEM;
	}

	json_object_object_foreach(jobj_keyslots, key, val) {

		if (!(jobj_area = json_contains(cd, val, key, "Keyslot", "area", json_type_object)) ||
		    !json_contains(cd, jobj_area, key, "Keyslot area", "type", json_type_string) ||
		    !(jobj_offset = json_contains(cd, jobj_area, key, "Keyslot", "offset", json_type_string)) ||
		    !(jobj_length = json_contains(cd, jobj_area, key, "Keyslot", "size", json_type_string)) ||
		    !numbered(cd, "offset", json_object_get_string(jobj_offset)) ||
		    !numbered(cd, "size", json_object_get_string(jobj_length))) {
			free(intervals);
			return 1;
		}

		/* rule out values > UINT64_MAX */
		if (!json_str_to_uint64(jobj_offset, &intervals[i].offset) ||
		    !json_str_to_uint64(jobj_length, &intervals[i].length)) {
			free(intervals);
			return 1;
		}

		i++;
	}

	if (length != i) {
		free(intervals);
		return 1;
	}

	ret = validate_intervals(cd, length, intervals, metadata_size, LUKS2_hdr_and_areas_size_jobj(hdr_jobj)) ? 0 : 1;

	free(intervals);

	return ret;
}


static json_bool validate_intervals(struct crypt_device *cd,
				    int length, const struct interval *ix,
				    uint64_t metadata_size, uint64_t keyslots_area_end)
{
	int j, i = 0;

	while (i < length) {
		if (ix[i].offset < 2 * metadata_size) {
			log_dbg(cd, "Illegal area offset: %" PRIu64 ".", ix[i].offset);
			return 0;
		}

		if (!ix[i].length) {
			log_dbg(cd, "Area length must be greater than zero.");
			return 0;
		}

		if ((ix[i].offset + ix[i].length) > keyslots_area_end) {
			log_dbg(cd, "Area [%" PRIu64 ", %" PRIu64 "] overflows binary keyslots area (ends at offset: %" PRIu64 ").",
				ix[i].offset, ix[i].offset + ix[i].length, keyslots_area_end);
			return 0;
		}

		for (j = 0; j < length; j++) {
			if (i == j)
				continue;
			if ((ix[i].offset >= ix[j].offset) && (ix[i].offset < (ix[j].offset + ix[j].length))) {
				log_dbg(cd, "Overlapping areas [%" PRIu64 ",%" PRIu64 "] and [%" PRIu64 ",%" PRIu64 "].",
					ix[i].offset, ix[i].offset + ix[i].length,
					ix[j].offset, ix[j].offset + ix[j].length);
				return 0;
			}
		}

		i++;
	}

	return 1;
}


xfs_attr_shortform_lookup(xfs_da_args_t *args)
{
	xfs_attr_shortform_t *sf;
	xfs_attr_sf_entry_t *sfe;
	int i;
	struct xfs_ifork *ifp;

	trace_xfs_attr_sf_lookup(args);

	ifp = args->dp->i_afp;
	ASSERT(ifp->if_flags & XFS_IFINLINE);
	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
	sfe = &sf->list[0];
	for (i = 0; i < sf->hdr.count;
				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
		if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
				sfe->flags))
			return -EEXIST;
	}
	return -ENOATTR;
}


xfs_attr3_leaf_clearflag(
	struct xfs_da_args	*args)
{
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_remote *name_rmt;
	struct xfs_buf		*bp;
	int			error;
#ifdef DEBUG
	struct xfs_attr3_icleaf_hdr ichdr;
	xfs_attr_leaf_name_local_t *name_loc;
	int namelen;
	char *name;
#endif /* DEBUG */

	trace_xfs_attr_leaf_clearflag(args);
	/*
	 * Set up the operation.
	 */
	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
	if (error)
		return error;

	leaf = bp->b_addr;
	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
	ASSERT(entry->flags & XFS_ATTR_INCOMPLETE);

#ifdef DEBUG
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
	ASSERT(args->index < ichdr.count);
	ASSERT(args->index >= 0);

	if (entry->flags & XFS_ATTR_LOCAL) {
		name_loc = xfs_attr3_leaf_name_local(leaf, args->index);
		namelen = name_loc->namelen;
		name = (char *)name_loc->nameval;
	} else {
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
		namelen = name_rmt->namelen;
		name = (char *)name_rmt->name;
	}
	ASSERT(be32_to_cpu(entry->hashval) == args->hashval);
	ASSERT(namelen == args->namelen);
	ASSERT(memcmp(name, args->name, namelen) == 0);
#endif /* DEBUG */

	entry->flags &= ~XFS_ATTR_INCOMPLETE;
	xfs_trans_log_buf(args->trans, bp,
			 XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));

	if (args->rmtblkno) {
		ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0);
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
		name_rmt->valueblk = cpu_to_be32(args->rmtblkno);
		name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen);
		xfs_trans_log_buf(args->trans, bp,
			 XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
	}

	return 0;
}


xfs_attr3_leaf_verify(
	struct xfs_buf			*bp)
{
	struct xfs_attr3_icleaf_hdr	ichdr;
	struct xfs_mount		*mp = bp->b_mount;
	struct xfs_attr_leafblock	*leaf = bp->b_addr;
	struct xfs_attr_leaf_entry	*entries;
	struct xfs_attr_leaf_entry	*ent;
	char				*buf_end;
	uint32_t			end;	/* must be 32bit - see below */
	__u32				last_hashval = 0;
	int				i;
	xfs_failaddr_t			fa;

	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);

	fa = xfs_da3_blkinfo_verify(bp, bp->b_addr);
	if (fa)
		return fa;

	/*
	 * firstused is the block offset of the first name info structure.
	 * Make sure it doesn't go off the block or crash into the header.
	 */
	if (ichdr.firstused > mp->m_attr_geo->blksize)
		return __this_address;
	if (ichdr.firstused < xfs_attr3_leaf_hdr_size(leaf))
		return __this_address;

	/* Make sure the entries array doesn't crash into the name info. */
	entries = xfs_attr3_leaf_entryp(bp->b_addr);
	if ((char *)&entries[ichdr.count] >
	    (char *)bp->b_addr + ichdr.firstused)
		return __this_address;

	/*
	 * NOTE: This verifier historically failed empty leaf buffers because
	 * we expect the fork to be in another format. Empty attr fork format
	 * conversions are possible during xattr set, however, and format
	 * conversion is not atomic with the xattr set that triggers it. We
	 * cannot assume leaf blocks are non-empty until that is addressed.
	*/
	buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize;
	for (i = 0, ent = entries; i < ichdr.count; ent++, i++) {
		fa = xfs_attr3_leaf_verify_entry(mp, buf_end, leaf, &ichdr,
				ent, i, &last_hashval);
		if (fa)
			return fa;
	}

	/*
	 * Quickly check the freemap information.  Attribute data has to be
	 * aligned to 4-byte boundaries, and likewise for the free space.
	 *
	 * Note that for 64k block size filesystems, the freemap entries cannot
	 * overflow as they are only be16 fields. However, when checking end
	 * pointer of the freemap, we have to be careful to detect overflows and
	 * so use uint32_t for those checks.
	 */
	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
		if (ichdr.freemap[i].base > mp->m_attr_geo->blksize)
			return __this_address;
		if (ichdr.freemap[i].base & 0x3)
			return __this_address;
		if (ichdr.freemap[i].size > mp->m_attr_geo->blksize)
			return __this_address;
		if (ichdr.freemap[i].size & 0x3)
			return __this_address;

		/* be care of 16 bit overflows here */
		end = (uint32_t)ichdr.freemap[i].base + ichdr.freemap[i].size;
		if (end < ichdr.freemap[i].base)
			return __this_address;
		if (end > mp->m_attr_geo->blksize)
			return __this_address;
	}

	return NULL;
}


static int __packet_rcv_has_room(const struct packet_sock *po,
				 const struct sk_buff *skb)
{
	const struct sock *sk = &po->sk;
	int ret = ROOM_NONE;

	if (po->prot_hook.func != tpacket_rcv) {
		int rcvbuf = READ_ONCE(sk->sk_rcvbuf);
		int avail = rcvbuf - atomic_read(&sk->sk_rmem_alloc)
				   - (skb ? skb->truesize : 0);

		if (avail > (rcvbuf >> ROOM_POW_OFF))
			return ROOM_NORMAL;
		else if (avail > 0)
			return ROOM_LOW;
		else
			return ROOM_NONE;
	}

	if (po->tp_version == TPACKET_V3) {
		if (__tpacket_v3_has_room(po, ROOM_POW_OFF))
			ret = ROOM_NORMAL;
		else if (__tpacket_v3_has_room(po, 0))
			ret = ROOM_LOW;
	} else {
		if (__tpacket_has_room(po, ROOM_POW_OFF))
			ret = ROOM_NORMAL;
		else if (__tpacket_has_room(po, 0))
			ret = ROOM_LOW;
	}

	return ret;
}


static bool fanout_flow_is_huge(struct packet_sock *po, struct sk_buff *skb)
{
	u32 *history = po->rollover->history;
	u32 victim, rxhash;
	int i, count = 0;

	rxhash = skb_get_hash(skb);
	for (i = 0; i < ROLLOVER_HLEN; i++)
		if (READ_ONCE(history[i]) == rxhash)
			count++;

	victim = prandom_u32() % ROLLOVER_HLEN;

	/* Avoid dirtying the cache line if possible */
	if (READ_ONCE(history[victim]) != rxhash)
		WRITE_ONCE(history[victim], rxhash);

	return count > (ROLLOVER_HLEN >> 1);
}


	__releases(&pkc->blk_fill_in_prog_lock)
{
	struct tpacket_kbdq_core *pkc  = GET_PBDQC_FROM_RB(rb);

	read_unlock(&pkc->blk_fill_in_prog_lock);
}


static void prb_retire_rx_blk_timer_expired(struct timer_list *t)
{
	struct packet_sock *po =
		from_timer(po, t, rx_ring.prb_bdqc.retire_blk_timer);
	struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(&po->rx_ring);
	unsigned int frozen;
	struct tpacket_block_desc *pbd;

	spin_lock(&po->sk.sk_receive_queue.lock);

	frozen = prb_queue_frozen(pkc);
	pbd = GET_CURR_PBLOCK_DESC_FROM_CORE(pkc);

	if (unlikely(pkc->delete_blk_timer))
		goto out;

	/* We only need to plug the race when the block is partially filled.
	 * tpacket_rcv:
	 *		lock(); increment BLOCK_NUM_PKTS; unlock()
	 *		copy_bits() is in progress ...
	 *		timer fires on other cpu:
	 *		we can't retire the current block because copy_bits
	 *		is in progress.
	 *
	 */
	if (BLOCK_NUM_PKTS(pbd)) {
		/* Waiting for skb_copy_bits to finish... */
		write_lock(&pkc->blk_fill_in_prog_lock);
		write_unlock(&pkc->blk_fill_in_prog_lock);
	}

	if (pkc->last_kactive_blk_num == pkc->kactive_blk_num) {
		if (!frozen) {
			if (!BLOCK_NUM_PKTS(pbd)) {
				/* An empty block. Just refresh the timer. */
				goto refresh_timer;
			}
			prb_retire_current_block(pkc, po, TP_STATUS_BLK_TMO);
			if (!prb_dispatch_next_block(pkc, po))
				goto refresh_timer;
			else
				goto out;
		} else {
			/* Case 1. Queue was frozen because user-space was
			 *	   lagging behind.
			 */
			if (prb_curr_blk_in_use(pbd)) {
				/*
				 * Ok, user-space is still behind.
				 * So just refresh the timer.
				 */
				goto refresh_timer;
			} else {
			       /* Case 2. queue was frozen,user-space caught up,
				* now the link went idle && the timer fired.
				* We don't have a block to close.So we open this
				* block and restart the timer.
				* opening a block thaws the queue,restarts timer
				* Thawing/timer-refresh is a side effect.
				*/
				prb_open_block(pkc, pbd);
				goto out;
			}
		}
	}

refresh_timer:
	_prb_refresh_rx_retire_blk_timer(pkc);

out:
	spin_unlock(&po->sk.sk_receive_queue.lock);
}


static void register_prot_hook(struct sock *sk)
{
	lockdep_assert_held_once(&pkt_sk(sk)->bind_lock);
	__register_prot_hook(sk);
}


static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
		      struct packet_type *pt, struct net_device *orig_dev)
{
	struct sock *sk;
	struct sockaddr_ll *sll;
	struct packet_sock *po;
	u8 *skb_head = skb->data;
	int skb_len = skb->len;
	unsigned int snaplen, res;
	bool is_drop_n_account = false;

	if (skb->pkt_type == PACKET_LOOPBACK)
		goto drop;

	sk = pt->af_packet_priv;
	po = pkt_sk(sk);

	if (!net_eq(dev_net(dev), sock_net(sk)))
		goto drop;

	skb->dev = dev;

	if (dev->header_ops) {
		/* The device has an explicit notion of ll header,
		 * exported to higher levels.
		 *
		 * Otherwise, the device hides details of its frame
		 * structure, so that corresponding packet head is
		 * never delivered to user.
		 */
		if (sk->sk_type != SOCK_DGRAM)
			skb_push(skb, skb->data - skb_mac_header(skb));
		else if (skb->pkt_type == PACKET_OUTGOING) {
			/* Special case: outgoing packets have ll header at head */
			skb_pull(skb, skb_network_offset(skb));
		}
	}

	snaplen = skb->len;

	res = run_filter(skb, sk, snaplen);
	if (!res)
		goto drop_n_restore;
	if (snaplen > res)
		snaplen = res;

	if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
		goto drop_n_acct;

	if (skb_shared(skb)) {
		struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
		if (nskb == NULL)
			goto drop_n_acct;

		if (skb_head != skb->data) {
			skb->data = skb_head;
			skb->len = skb_len;
		}
		consume_skb(skb);
		skb = nskb;
	}

	sock_skb_cb_check_size(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8);

	sll = &PACKET_SKB_CB(skb)->sa.ll;
	sll->sll_hatype = dev->type;
	sll->sll_pkttype = skb->pkt_type;
	if (unlikely(po->origdev))
		sll->sll_ifindex = orig_dev->ifindex;
	else
		sll->sll_ifindex = dev->ifindex;

	sll->sll_halen = dev_parse_header(skb, sll->sll_addr);

	/* sll->sll_family and sll->sll_protocol are set in packet_recvmsg().
	 * Use their space for storing the original skb length.
	 */
	PACKET_SKB_CB(skb)->sa.origlen = skb->len;

	if (pskb_trim(skb, snaplen))
		goto drop_n_acct;

	skb_set_owner_r(skb, sk);
	skb->dev = NULL;
	skb_dst_drop(skb);

	/* drop conntrack reference */
	nf_reset_ct(skb);

	spin_lock(&sk->sk_receive_queue.lock);
	po->stats.stats1.tp_packets++;
	sock_skb_set_dropcount(sk, skb);
	__skb_queue_tail(&sk->sk_receive_queue, skb);
	spin_unlock(&sk->sk_receive_queue.lock);
	sk->sk_data_ready(sk);
	return 0;

drop_n_acct:
	is_drop_n_account = true;
	atomic_inc(&po->tp_drops);
	atomic_inc(&sk->sk_drops);

drop_n_restore:
	if (skb_head != skb->data && skb_shared(skb)) {
		skb->data = skb_head;
		skb->len = skb_len;
	}
drop:
	if (!is_drop_n_account)
		consume_skb(skb);
	else
		kfree_skb(skb);
	return 0;
}


static void *prb_lookup_block(const struct packet_sock *po,
			      const struct packet_ring_buffer *rb,
			      unsigned int idx,
			      int status)
{
	struct tpacket_kbdq_core *pkc  = GET_PBDQC_FROM_RB(rb);
	struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, idx);

	if (status != BLOCK_STATUS(pbd))
		return NULL;
	return pbd;
}


static int __packet_get_status(const struct packet_sock *po, void *frame)
{
	union tpacket_uhdr h;

	smp_rmb();

	h.raw = frame;
	switch (po->tp_version) {
	case TPACKET_V1:
		flush_dcache_page(pgv_to_page(&h.h1->tp_status));
		return h.h1->tp_status;
	case TPACKET_V2:
		flush_dcache_page(pgv_to_page(&h.h2->tp_status));
		return h.h2->tp_status;
	case TPACKET_V3:
		flush_dcache_page(pgv_to_page(&h.h3->tp_status));
		return h.h3->tp_status;
	default:
		WARN(1, "TPACKET version not supported.\n");
		BUG();
		return 0;
	}
}


static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame,
				    struct sk_buff *skb)
{
	union tpacket_uhdr h;
	struct timespec64 ts;
	__u32 ts_status;

	if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp)))
		return 0;

	h.raw = frame;
	/*
	 * versions 1 through 3 overflow the timestamps in y2106, since they
	 * all store the seconds in a 32-bit unsigned integer.
	 * If we create a version 4, that should have a 64-bit timestamp,
	 * either 64-bit seconds + 32-bit nanoseconds, or just 64-bit
	 * nanoseconds.
	 */
	switch (po->tp_version) {
	case TPACKET_V1:
		h.h1->tp_sec = ts.tv_sec;
		h.h1->tp_usec = ts.tv_nsec / NSEC_PER_USEC;
		break;
	case TPACKET_V2:
		h.h2->tp_sec = ts.tv_sec;
		h.h2->tp_nsec = ts.tv_nsec;
		break;
	case TPACKET_V3:
		h.h3->tp_sec = ts.tv_sec;
		h.h3->tp_nsec = ts.tv_nsec;
		break;
	default:
		WARN(1, "TPACKET version not supported.\n");
		BUG();
	}

	/* one flush is safe, as both fields always lie on the same cacheline */
	flush_dcache_page(pgv_to_page(&h.h1->tp_sec));
	smp_wmb();

	return ts_status;
}


ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb)
{
	return ctnetlink_dump_list(skb, cb, true);
}


ctnetlink_change_seq_adj(struct nf_conn *ct,
			 const struct nlattr * const cda[])
{
	struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
	int ret = 0;

	if (!seqadj)
		return 0;

	spin_lock_bh(&ct->lock);
	if (cda[CTA_SEQ_ADJ_ORIG]) {
		ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_ORIGINAL],
				     cda[CTA_SEQ_ADJ_ORIG]);
		if (ret < 0)
			goto err;

		set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
	}

	if (cda[CTA_SEQ_ADJ_REPLY]) {
		ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_REPLY],
				     cda[CTA_SEQ_ADJ_REPLY]);
		if (ret < 0)
			goto err;

		set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
	}

	spin_unlock_bh(&ct->lock);
	return 0;
err:
	spin_unlock_bh(&ct->lock);
	return ret;
}


static int ctnetlink_flush_iterate(struct nf_conn *ct, void *data)
{
	if (test_bit(IPS_OFFLOAD_BIT, &ct->status))
		return 0;

	return ctnetlink_filter_match(ct, data);
}


ctnetlink_create_expect(struct net *net,
			const struct nf_conntrack_zone *zone,
			const struct nlattr * const cda[],
			u_int8_t u3, u32 portid, int report)
{
	struct nf_conntrack_tuple tuple, mask, master_tuple;
	struct nf_conntrack_tuple_hash *h = NULL;
	struct nf_conntrack_helper *helper = NULL;
	struct nf_conntrack_expect *exp;
	struct nf_conn *ct;
	int err;

	/* caller guarantees that those three CTA_EXPECT_* exist */
	err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE,
				    u3, NULL);
	if (err < 0)
		return err;
	err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK,
				    u3, NULL);
	if (err < 0)
		return err;
	err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER,
				    u3, NULL);
	if (err < 0)
		return err;

	/* Look for master conntrack of this expectation */
	h = nf_conntrack_find_get(net, zone, &master_tuple);
	if (!h)
		return -ENOENT;
	ct = nf_ct_tuplehash_to_ctrack(h);

	rcu_read_lock();
	if (cda[CTA_EXPECT_HELP_NAME]) {
		const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);

		helper = __nf_conntrack_helper_find(helpname, u3,
						    nf_ct_protonum(ct));
		if (helper == NULL) {
			rcu_read_unlock();
#ifdef CONFIG_MODULES
			if (request_module("nfct-helper-%s", helpname) < 0) {
				err = -EOPNOTSUPP;
				goto err_ct;
			}
			rcu_read_lock();
			helper = __nf_conntrack_helper_find(helpname, u3,
							    nf_ct_protonum(ct));
			if (helper) {
				err = -EAGAIN;
				goto err_rcu;
			}
			rcu_read_unlock();
#endif
			err = -EOPNOTSUPP;
			goto err_ct;
		}
	}

	exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask);
	if (IS_ERR(exp)) {
		err = PTR_ERR(exp);
		goto err_rcu;
	}

	err = nf_ct_expect_related_report(exp, portid, report, 0);
	nf_ct_expect_put(exp);
err_rcu:
	rcu_read_unlock();
err_ct:
	nf_ct_put(ct);
	return err;
}


ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb)
{
	return ctnetlink_dump_list(skb, cb, false);
}


ctnetlink_setup_nat(struct nf_conn *ct, const struct nlattr * const cda[])
{
#if IS_ENABLED(CONFIG_NF_NAT)
	int ret;

	if (!cda[CTA_NAT_DST] && !cda[CTA_NAT_SRC])
		return 0;

	ret = ctnetlink_parse_nat_setup(ct, NF_NAT_MANIP_DST,
					cda[CTA_NAT_DST]);
	if (ret < 0)
		return ret;

	return ctnetlink_parse_nat_setup(ct, NF_NAT_MANIP_SRC,
					 cda[CTA_NAT_SRC]);
#else
	if (!cda[CTA_NAT_DST] && !cda[CTA_NAT_SRC])
		return 0;
	return -EOPNOTSUPP;
#endif
}


ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct)
{
	struct nf_conn_labels *labels = nf_ct_labels_find(ct);
	unsigned int i;

	if (!labels)
		return 0;

	i = 0;
	do {
		if (labels->bits[i] != 0)
			return nla_put(skb, CTA_LABELS, sizeof(labels->bits),
				       labels->bits);
		i++;
	} while (i < ARRAY_SIZE(labels->bits));

	return 0;
}


static int __init ctnetlink_init(void)
{
	int ret;

	ret = nfnetlink_subsys_register(&ctnl_subsys);
	if (ret < 0) {
		pr_err("ctnetlink_init: cannot register with nfnetlink.\n");
		goto err_out;
	}

	ret = nfnetlink_subsys_register(&ctnl_exp_subsys);
	if (ret < 0) {
		pr_err("ctnetlink_init: cannot register exp with nfnetlink.\n");
		goto err_unreg_subsys;
	}

	ret = register_pernet_subsys(&ctnetlink_net_ops);
	if (ret < 0) {
		pr_err("ctnetlink_init: cannot register pernet operations\n");
		goto err_unreg_exp_subsys;
	}
#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
	/* setup interaction between nf_queue and nf_conntrack_netlink. */
	RCU_INIT_POINTER(nfnl_ct_hook, &ctnetlink_glue_hook);
#endif
	return 0;

err_unreg_exp_subsys:
	nfnetlink_subsys_unregister(&ctnl_exp_subsys);
err_unreg_subsys:
	nfnetlink_subsys_unregister(&ctnl_subsys);
err_out:
	return ret;
}


static int ctnetlink_start(struct netlink_callback *cb)
{
	const struct nlattr * const *cda = cb->data;
	struct ctnetlink_filter *filter = NULL;
	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
	u8 family = nfmsg->nfgen_family;

	if (ctnetlink_needs_filter(family, cda)) {
		filter = ctnetlink_alloc_filter(cda, family);
		if (IS_ERR(filter))
			return PTR_ERR(filter);
	}

	cb->data = filter;
	return 0;
}


static int ctnetlink_dump_status(struct sk_buff *skb, const struct nf_conn *ct)
{
	if (nla_put_be32(skb, CTA_STATUS, htonl(ct->status)))
		goto nla_put_failure;
	return 0;

nla_put_failure:
	return -1;
}


static int ctnetlink_stat_ct_cpu(struct net *net, struct sock *ctnl,
				 struct sk_buff *skb,
				 const struct nlmsghdr *nlh,
				 const struct nlattr * const cda[],
				 struct netlink_ext_ack *extack)
{
	if (nlh->nlmsg_flags & NLM_F_DUMP) {
		struct netlink_dump_control c = {
			.dump = ctnetlink_ct_stat_cpu_dump,
		};
		return netlink_dump_start(ctnl, skb, nlh, &c);
	}

	return 0;
}


static inline int ctnetlink_secctx_size(const struct nf_conn *ct)
{
#ifdef CONFIG_NF_CONNTRACK_SECMARK
	int len, ret;

	ret = security_secid_to_secctx(ct->secmark, NULL, &len);
	if (ret)
		return 0;

	return nla_total_size(0) /* CTA_SECCTX */
	       + nla_total_size(sizeof(char) * len); /* CTA_SECCTX_NAME */
#else
	return 0;
#endif
}


ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct)
{
	struct nlattr *cda[CTA_MAX+1];
	int ret;

	ret = nla_parse_nested_deprecated(cda, CTA_MAX, attr, ct_nla_policy,
					  NULL);
	if (ret < 0)
		return ret;

	return ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct);
}


static int ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, struct nf_conn *ct)
{
	struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
	struct nf_ct_seqadj *seq;

	if (!(ct->status & IPS_SEQ_ADJUST) || !seqadj)
		return 0;

	spin_lock_bh(&ct->lock);
	seq = &seqadj->seq[IP_CT_DIR_ORIGINAL];
	if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_ORIG) == -1)
		goto err;

	seq = &seqadj->seq[IP_CT_DIR_REPLY];
	if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_REPLY) == -1)
		goto err;

	spin_unlock_bh(&ct->lock);
	return 0;
err:
	spin_unlock_bh(&ct->lock);
	return -1;
}


int nfs4_proc_destroy_session(struct nfs4_session *session,
		const struct cred *cred)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION],
		.rpc_argp = session,
		.rpc_cred = cred,
	};
	int status = 0;

	dprintk("--> nfs4_proc_destroy_session\n");

	/* session is still being setup */
	if (!test_and_clear_bit(NFS4_SESSION_ESTABLISHED, &session->session_state))
		return 0;

	status = rpc_call_sync(session->clp->cl_rpcclient, &msg,
			       RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN);
	trace_nfs4_destroy_session(session->clp, status);

	if (status)
		dprintk("NFS: Got error %d from the server on DESTROY_SESSION. "
			"Session has been destroyed regardless...\n", status);

	dprintk("<-- nfs4_proc_destroy_session\n");
	return status;
}


static int nfs4_xdr_dec_fsid_present(struct rpc_rqst *rqstp,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs4_fsid_present_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_getfh(xdr, res->fh);
	if (status)
		goto out;
	if (res->renew)
		status = decode_renew(xdr);
out:
	return status;
}


static int nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred,
		u64 cookie, struct page **pages, unsigned int count, bool plus)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = _nfs4_proc_readdir(dentry, cred, cookie,
				pages, count, plus);
		trace_nfs4_readdir(d_inode(dentry), err);
		err = nfs4_handle_exception(NFS_SERVER(d_inode(dentry)), err,
				&exception);
	} while (exception.retry);
	return err;
}


_nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
{
	struct inode *inode = data->state->inode;
	struct nfs4_state *state = data->state;
	int ret;

	if (!data->rpc_done) {
		if (data->rpc_status)
			return ERR_PTR(data->rpc_status);
		/* cached opens have already been processed */
		goto update;
	}

	ret = nfs_refresh_inode(inode, &data->f_attr);
	if (ret)
		return ERR_PTR(ret);

	if (data->o_res.delegation_type != 0)
		nfs4_opendata_check_deleg(data, state);
update:
	if (!update_open_stateid(state, &data->o_res.stateid,
				NULL, data->o_arg.fmode))
		return ERR_PTR(-EAGAIN);
	refcount_inc(&state->count);

	return state;
}


static void nfs41_free_stateid_release(void *calldata)
{
	kfree(calldata);
}


static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
		const nfs4_stateid *stateid)
{
	u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
	u32 stateid_seqid = be32_to_cpu(stateid->seqid);

	if (stateid_seqid == state_seqid + 1U ||
	    (stateid_seqid == 1U && state_seqid == 0xffffffffU))
		nfs_state_log_update_open_stateid(state);
	else
		set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
}


nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
		 int flags)
{
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs4_label l, *ilabel = NULL;
	struct nfs_open_context *ctx;
	struct nfs4_state *state;
	int status = 0;

	ctx = alloc_nfs_open_context(dentry, FMODE_READ, NULL);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);

	ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);

	if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
		sattr->ia_mode &= ~current_umask();
	state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL);
	if (IS_ERR(state)) {
		status = PTR_ERR(state);
		goto out;
	}
out:
	nfs4_label_release_security(ilabel);
	put_nfs_open_context(ctx);
	return status;
}


static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_fsinfo_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_sequence(xdr, &res->seq_res, req);
	if (!status)
		status = decode_putfh(xdr);
	if (!status)
		status = decode_fsinfo(xdr, res->fsinfo);
	return status;
}


static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
				   const struct qstr *name,
				   struct nfs4_fs_locations *fs_locations,
				   struct page *page)
{
	struct nfs_server *server = NFS_SERVER(dir);
	u32 bitmask[3];
	struct nfs4_fs_locations_arg args = {
		.dir_fh = NFS_FH(dir),
		.name = name,
		.page = page,
		.bitmask = bitmask,
	};
	struct nfs4_fs_locations_res res = {
		.fs_locations = fs_locations,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	int status;

	dprintk("%s: start\n", __func__);

	bitmask[0] = nfs4_fattr_bitmap[0] | FATTR4_WORD0_FS_LOCATIONS;
	bitmask[1] = nfs4_fattr_bitmap[1];

	/* Ask for the fileid of the absent filesystem if mounted_on_fileid
	 * is not supported */
	if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
		bitmask[0] &= ~FATTR4_WORD0_FILEID;
	else
		bitmask[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;

	nfs_fattr_init(&fs_locations->fattr);
	fs_locations->server = server;
	fs_locations->nlocations = 0;
	status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
	dprintk("%s: returned status = %d\n", __func__, status);
	return status;
}


static int decode_test_stateid(struct xdr_stream *xdr,
			       struct nfs41_test_stateid_res *res)
{
	__be32 *p;
	int status;
	int num_res;

	status = decode_op_hdr(xdr, OP_TEST_STATEID);
	if (status)
		return status;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	num_res = be32_to_cpup(p++);
	if (num_res != 1)
		return -EIO;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	res->status = be32_to_cpup(p++);

	return status;
}


static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
{
	u32 n;
	__be32 *p;
	int status = 0;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	n = be32_to_cpup(p);
	if (n == 0)
		goto root_path;
	dprintk("pathname4: ");
	if (n > NFS4_PATHNAME_MAXCOMPONENTS) {
		dprintk("cannot parse %d components in path\n", n);
		goto out_eio;
	}
	for (path->ncomponents = 0; path->ncomponents < n; path->ncomponents++) {
		struct nfs4_string *component = &path->components[path->ncomponents];
		status = decode_opaque_inline(xdr, &component->len, &component->data);
		if (unlikely(status != 0))
			goto out_eio;
		ifdebug (XDR)
			pr_cont("%s%.*s ",
				(path->ncomponents != n ? "/ " : ""),
				component->len, component->data);
	}
out:
	return status;
root_path:
/* a root pathname is sent as a zero component4 */
	path->ncomponents = 1;
	path->components[0].len=0;
	path->components[0].data=NULL;
	dprintk("pathname4: /\n");
	goto out;
out_eio:
	dprintk(" status %d", status);
	status = -EIO;
	goto out;
}


static int nfs41_proc_async_sequence(struct nfs_client *clp, const struct cred *cred, unsigned renew_flags)
{
	struct rpc_task *task;
	int ret = 0;

	if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
		return -EAGAIN;
	task = _nfs41_proc_sequence(clp, cred, NULL, false);
	if (IS_ERR(task))
		ret = PTR_ERR(task);
	else
		rpc_put_task_async(task);
	dprintk("<-- %s status=%d\n", __func__, ret);
	return ret;
}