cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
{
        enum CRStatus status = CR_OK;
        CRToken *token = NULL;
        CRInputPos init_pos;
        guint32 next_char = 0;
        guchar next_bytes[4] = { 0 };
        gboolean reached_eof = FALSE;
        CRInput *input = NULL;
        CRString *str = NULL;
        CRRgb *rgb = NULL;
        CRParsingLocation location = {0} ;

        g_return_val_if_fail (a_this && PRIVATE (a_this)
                              && a_tk && *a_tk == NULL
                              && PRIVATE (a_this)->input,
                              CR_BAD_PARAM_ERROR);

        if (PRIVATE (a_this)->token_cache) {
                *a_tk = PRIVATE (a_this)->token_cache;
                PRIVATE (a_this)->token_cache = NULL;
                return CR_OK;
        }

        RECORD_INITIAL_POS (a_this, &init_pos);

        status = cr_input_get_end_of_file
                (PRIVATE (a_this)->input, &reached_eof);
        ENSURE_PARSING_COND (status == CR_OK);

        if (reached_eof == TRUE) {
                status = CR_END_OF_INPUT_ERROR;
                goto error;
        }

        input = PRIVATE (a_this)->input;

        PEEK_NEXT_CHAR (a_this, &next_char);
        token = cr_token_new ();
        ENSURE_PARSING_COND (token);

        switch (next_char) {
        case '@':
                {
                        if (BYTE (input, 2, NULL) == 'f'
                            && BYTE (input, 3, NULL) == 'o'
                            && BYTE (input, 4, NULL) == 'n'
                            && BYTE (input, 5, NULL) == 't'
                            && BYTE (input, 6, NULL) == '-'
                            && BYTE (input, 7, NULL) == 'f'
                            && BYTE (input, 8, NULL) == 'a'
                            && BYTE (input, 9, NULL) == 'c'
                            && BYTE (input, 10, NULL) == 'e') {
                                SKIP_CHARS (a_this, 1);
                                cr_tknzr_get_parsing_location
                                        (a_this, &location) ;
                                SKIP_CHARS (a_this, 9);
                                status = cr_token_set_font_face_sym (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_parsing_location_copy (&token->location,
                                                          &location) ;
                                goto done;
                        }

                        if (BYTE (input, 2, NULL) == 'c'
                            && BYTE (input, 3, NULL) == 'h'
                            && BYTE (input, 4, NULL) == 'a'
                            && BYTE (input, 5, NULL) == 'r'
                            && BYTE (input, 6, NULL) == 's'
                            && BYTE (input, 7, NULL) == 'e'
                            && BYTE (input, 8, NULL) == 't') {
                                SKIP_CHARS (a_this, 1);
                                cr_tknzr_get_parsing_location
                                        (a_this, &location) ;
                                SKIP_CHARS (a_this, 7);
                                status = cr_token_set_charset_sym (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_parsing_location_copy (&token->location,
                                                          &location) ;
                                goto done;
                        }

                        if (BYTE (input, 2, NULL) == 'i'
                            && BYTE (input, 3, NULL) == 'm'
                            && BYTE (input, 4, NULL) == 'p'
                            && BYTE (input, 5, NULL) == 'o'
                            && BYTE (input, 6, NULL) == 'r'
                            && BYTE (input, 7, NULL) == 't') {
                                SKIP_CHARS (a_this, 1);
                                cr_tknzr_get_parsing_location
                                        (a_this, &location) ;
                                SKIP_CHARS (a_this, 6);
                                status = cr_token_set_import_sym (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_parsing_location_copy (&token->location,
                                                          &location) ;
                                goto done;
                        }

                        if (BYTE (input, 2, NULL) == 'm'
                            && BYTE (input, 3, NULL) == 'e'
                            && BYTE (input, 4, NULL) == 'd'
                            && BYTE (input, 5, NULL) == 'i'
                            && BYTE (input, 6, NULL) == 'a') {
                                SKIP_CHARS (a_this, 1);
                                cr_tknzr_get_parsing_location (a_this,
                                                               &location) ;
                                SKIP_CHARS (a_this, 5);
                                status = cr_token_set_media_sym (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_parsing_location_copy (&token->location,
                                                          &location) ;
                                goto done;
                        }

                        if (BYTE (input, 2, NULL) == 'p'
                            && BYTE (input, 3, NULL) == 'a'
                            && BYTE (input, 4, NULL) == 'g'
                            && BYTE (input, 5, NULL) == 'e') {
                                SKIP_CHARS (a_this, 1);
                                cr_tknzr_get_parsing_location (a_this,
                                                               &location) ;
                                SKIP_CHARS (a_this, 4);
                                status = cr_token_set_page_sym (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_parsing_location_copy (&token->location,
                                                          &location) ;
                                goto done;
                        }
                        status = cr_tknzr_parse_atkeyword (a_this, &str);
                        if (status == CR_OK) {
                                status = cr_token_set_atkeyword (token, str);
                                CHECK_PARSING_STATUS (status, TRUE);
                                if (str) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &str->location) ;
                                }
                                goto done;
                        }
                }
                break;

        case 'u':

                if (BYTE (input, 2, NULL) == 'r'
                    && BYTE (input, 3, NULL) == 'l'
                    && BYTE (input, 4, NULL) == '(') {
                        CRString *str2 = NULL;

                        status = cr_tknzr_parse_uri (a_this, &str2);
                        if (status == CR_OK) {
                                status = cr_token_set_uri (token, str2);
                                CHECK_PARSING_STATUS (status, TRUE);
                                if (str2) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &str2->location) ;
                                }
                                goto done;
                        }
                }
                goto fallback;
                break;

        case 'r':
                if (BYTE (input, 2, NULL) == 'g'
                    && BYTE (input, 3, NULL) == 'b'
                    && BYTE (input, 4, NULL) == '(') {
                        status = cr_tknzr_parse_rgb (a_this, &rgb);
                        if (status == CR_OK && rgb) {
                                status = cr_token_set_rgb (token, rgb);
                                CHECK_PARSING_STATUS (status, TRUE);
                                if (rgb) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &rgb->location) ;
                                }
                                rgb = NULL;
                                goto done;
                        }

                }
                goto fallback;
                break;

        case '<':
                if (BYTE (input, 2, NULL) == '!'
                    && BYTE (input, 3, NULL) == '-'
                    && BYTE (input, 4, NULL) == '-') {
                        SKIP_CHARS (a_this, 1);
                        cr_tknzr_get_parsing_location (a_this,
                                                       &location) ;
                        SKIP_CHARS (a_this, 3);
                        status = cr_token_set_cdo (token);
                        CHECK_PARSING_STATUS (status, TRUE);
                        cr_parsing_location_copy (&token->location,
                                                  &location) ;
                        goto done;
                }
                break;

        case '-':
                if (BYTE (input, 2, NULL) == '-'
                    && BYTE (input, 3, NULL) == '>') {
                        SKIP_CHARS (a_this, 1);
                        cr_tknzr_get_parsing_location (a_this,
                                                       &location) ;
                        SKIP_CHARS (a_this, 2);
                        status = cr_token_set_cdc (token);
                        CHECK_PARSING_STATUS (status, TRUE);
                        cr_parsing_location_copy (&token->location,
                                                  &location) ;
                        goto done;
                } else {
                        status = cr_tknzr_parse_ident
                                (a_this, &str);
                        if (status == CR_OK) {
                                cr_token_set_ident
                                        (token, str);
                                if (str) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &str->location) ;
                                }
                                goto done;
                        } else {
                                goto parse_number;
                        }
                }
                break;

        case '~':
                if (BYTE (input, 2, NULL) == '=') {
                        SKIP_CHARS (a_this, 1);
                        cr_tknzr_get_parsing_location (a_this,
                                                       &location) ;
                        SKIP_CHARS (a_this, 1);
                        status = cr_token_set_includes (token);
                        CHECK_PARSING_STATUS (status, TRUE);
                        cr_parsing_location_copy (&token->location,
                                                  &location) ;
                        goto done;
                }
                break;

        case '|':
                if (BYTE (input, 2, NULL) == '=') {
                        SKIP_CHARS (a_this, 1);
                        cr_tknzr_get_parsing_location (a_this,
                                                       &location) ;
                        SKIP_CHARS (a_this, 1);
                        status = cr_token_set_dashmatch (token);
                        CHECK_PARSING_STATUS (status, TRUE);
                        cr_parsing_location_copy (&token->location,
                                                  &location) ;
                        goto done;
                }
                break;

        case '/':
                if (BYTE (input, 2, NULL) == '*') {
                        status = cr_tknzr_parse_comment (a_this, &str);

                        if (status == CR_OK) {
                                status = cr_token_set_comment (token, str);
                                str = NULL;
                                CHECK_PARSING_STATUS (status, TRUE);
                                if (str) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &str->location) ;
                                }
                                goto done;
                        }
                }
                break ;

        case ';':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_semicolon (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case '{':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_cbo (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                goto done;

        case '}':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_cbc (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case '(':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_po (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case ')':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_pc (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case '[':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_bo (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case ']':
                SKIP_CHARS (a_this, 1);
                cr_tknzr_get_parsing_location (a_this,
                                               &location) ;
                status = cr_token_set_bc (token);
                CHECK_PARSING_STATUS (status, TRUE);
                cr_parsing_location_copy (&token->location,
                                          &location) ;
                goto done;

        case ' ':
        case '\t':
        case '\n':
        case '\f':
        case '\r':
                {
                        guchar *start = NULL,
                                *end = NULL;

                        status = cr_tknzr_parse_w (a_this, &start,
                                                   &end, &location);
                        if (status == CR_OK) {
                                status = cr_token_set_s (token);
                                CHECK_PARSING_STATUS (status, TRUE);
                                cr_tknzr_get_parsing_location (a_this,
                                                               &location) ;
                                goto done;
                        }
                }
                break;

        case '#':
                {
                        status = cr_tknzr_parse_hash (a_this, &str);
                        if (status == CR_OK && str) {
                                status = cr_token_set_hash (token, str);
                                CHECK_PARSING_STATUS (status, TRUE);
                                if (str) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &str->location) ;
                                }
                                str = NULL;
                                goto done;
                        }
                }
                break;

        case '\'':
        case '"':
                status = cr_tknzr_parse_string (a_this, &str);
                if (status == CR_OK && str) {
                        status = cr_token_set_string (token, str);
                        CHECK_PARSING_STATUS (status, TRUE);
                        if (str) {
                                cr_parsing_location_copy (&token->location,
                                                          &str->location) ;
                        }
                        str = NULL;
                        goto done;
                }
                break;

        case '!':
                status = cr_tknzr_parse_important (a_this, &location);
                if (status == CR_OK) {
                        status = cr_token_set_important_sym (token);
                        CHECK_PARSING_STATUS (status, TRUE);
                        cr_parsing_location_copy (&token->location,
                                                  &location) ;
                        goto done;
                }
                break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '.':
        case '+':
        /* '-' case is handled separately above for --> comments */
        parse_number:
                {
                        CRNum *num = NULL;

                        status = cr_tknzr_parse_num (a_this, &num);
                        if (status == CR_OK && num) {
                                next_bytes[0] = BYTE (input, 1, NULL);
                                next_bytes[1] = BYTE (input, 2, NULL);
                                next_bytes[2] = BYTE (input, 3, NULL);
                                next_bytes[3] = BYTE (input, 4, NULL);

                                if (next_bytes[0] == 'e'
                                    && next_bytes[1] == 'm') {
                                        num->type = NUM_LENGTH_EM;
                                        status = cr_token_set_ems (token,
                                                                   num);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'e'
                                           && next_bytes[1] == 'x') {
                                        num->type = NUM_LENGTH_EX;
                                        status = cr_token_set_exs (token,
                                                                   num);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'p'
                                           && next_bytes[1] == 'x') {
                                        num->type = NUM_LENGTH_PX;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_PX_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'c'
                                           && next_bytes[1] == 'm') {
                                        num->type = NUM_LENGTH_CM;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_CM_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'm'
                                           && next_bytes[1] == 'm') {
                                        num->type = NUM_LENGTH_MM;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_MM_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'i'
                                           && next_bytes[1] == 'n') {
                                        num->type = NUM_LENGTH_IN;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_IN_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'p'
                                           && next_bytes[1] == 't') {
                                        num->type = NUM_LENGTH_PT;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_PT_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'p'
                                           && next_bytes[1] == 'c') {
                                        num->type = NUM_LENGTH_PC;
                                        status = cr_token_set_length
                                                (token, num, LENGTH_PC_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'd'
                                           && next_bytes[1] == 'e'
                                           && next_bytes[2] == 'g') {
                                        num->type = NUM_ANGLE_DEG;
                                        status = cr_token_set_angle
                                                (token, num, ANGLE_DEG_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 3);
                                } else if (next_bytes[0] == 'r'
                                           && next_bytes[1] == 'a'
                                           && next_bytes[2] == 'd') {
                                        num->type = NUM_ANGLE_RAD;
                                        status = cr_token_set_angle
                                                (token, num, ANGLE_RAD_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 3);
                                } else if (next_bytes[0] == 'g'
                                           && next_bytes[1] == 'r'
                                           && next_bytes[2] == 'a'
                                           && next_bytes[3] == 'd') {
                                        num->type = NUM_ANGLE_GRAD;
                                        status = cr_token_set_angle
                                                (token, num, ANGLE_GRAD_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 4);
                                } else if (next_bytes[0] == 'm'
                                           && next_bytes[1] == 's') {
                                        num->type = NUM_TIME_MS;
                                        status = cr_token_set_time
                                                (token, num, TIME_MS_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 's') {
                                        num->type = NUM_TIME_S;
                                        status = cr_token_set_time
                                                (token, num, TIME_S_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 1);
                                } else if (next_bytes[0] == 'H'
                                           && next_bytes[1] == 'z') {
                                        num->type = NUM_FREQ_HZ;
                                        status = cr_token_set_freq
                                                (token, num, FREQ_HZ_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 2);
                                } else if (next_bytes[0] == 'k'
                                           && next_bytes[1] == 'H'
                                           && next_bytes[2] == 'z') {
                                        num->type = NUM_FREQ_KHZ;
                                        status = cr_token_set_freq
                                                (token, num, FREQ_KHZ_ET);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 3);
                                } else if (next_bytes[0] == '%') {
                                        num->type = NUM_PERCENTAGE;
                                        status = cr_token_set_percentage
                                                (token, num);
                                        num = NULL;
                                        SKIP_CHARS (a_this, 1);
                                } else {
                                        status = cr_tknzr_parse_ident (a_this,
                                                                       &str);
                                        if (status == CR_OK && str) {
                                                num->type = NUM_UNKNOWN_TYPE;
                                                status = cr_token_set_dimen
                                                        (token, num, str);
                                                num = NULL;
                                                CHECK_PARSING_STATUS (status,
                                                                      TRUE);
                                                str = NULL;
                                        } else {
                                                status = cr_token_set_number
                                                        (token, num);
                                                num = NULL;
                                                CHECK_PARSING_STATUS (status, CR_OK);
                                                str = NULL;
                                        }
                                }
                                if (token && token->u.num) {
                                        cr_parsing_location_copy (&token->location,
                                                                  &token->u.num->location) ;
                                } else {
                                        status = CR_ERROR ;
                                }
                                goto done ;
                        }
                }
                break;

        default:
        fallback:
                /*process the fallback cases here */

                if (next_char == '\\'
                    || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
                    || ((next_char >= 'a') && (next_char <= 'z'))
                    || ((next_char >= 'A') && (next_char <= 'Z'))) {
                        status = cr_tknzr_parse_ident (a_this, &str);
                        if (status == CR_OK && str) {
                                guint32 next_c = 0;

                                status = cr_input_peek_char
                                        (PRIVATE (a_this)->input, &next_c);

                                if (status == CR_OK && next_c == '(') {

                                        SKIP_CHARS (a_this, 1);
                                        status = cr_token_set_function
                                                (token, str);
                                        CHECK_PARSING_STATUS (status, TRUE);
                                        /*ownership is transfered
                                         *to token by cr_token_set_function.
                                         */
                                        if (str) {
                                                cr_parsing_location_copy (&token->location,
                                                                          &str->location) ;
                                        }
                                        str = NULL;
                                } else {
                                        status = cr_token_set_ident (token,
                                                                     str);
                                        CHECK_PARSING_STATUS (status, TRUE);
                                        if (str) {
                                                cr_parsing_location_copy (&token->location,
                                                                          &str->location) ;
                                        }
                                        str = NULL;
                                }
                                goto done;
                        } else {
                                if (str) {
                                        cr_string_destroy (str);
                                        str = NULL;
                                }
                        }
                }
                break;
        }

        READ_NEXT_CHAR (a_this, &next_char);
        cr_tknzr_get_parsing_location (a_this,
                                       &location) ;
        status = cr_token_set_delim (token, next_char);
        CHECK_PARSING_STATUS (status, TRUE);
        cr_parsing_location_copy (&token->location,
                                  &location) ;
 done:

        if (status == CR_OK && token) {
                *a_tk = token;
                /*
                 *store the previous position input stream pos.
                 */
                memmove (&PRIVATE (a_this)->prev_pos,
                         &init_pos, sizeof (CRInputPos));
                return CR_OK;
        }

 error:
        if (token) {
                cr_token_destroy (token);
                token = NULL;
        }

        if (str) {
                cr_string_destroy (str);
                str = NULL;
        }
        cr_tknzr_set_cur_pos (a_this, &init_pos);
        return status;

}


cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
{
        g_return_val_if_fail (a_this && PRIVATE (a_this)
                              && PRIVATE (a_this)->input && a_byte,
                              CR_BAD_PARAM_ERROR);

        if (PRIVATE (a_this)->token_cache) {
                cr_input_set_cur_pos (PRIVATE (a_this)->input,
                                      &PRIVATE (a_this)->prev_pos);
                cr_token_destroy (PRIVATE (a_this)->token_cache);
                PRIVATE (a_this)->token_cache = NULL;
        }

        return cr_input_peek_byte (PRIVATE (a_this)->input,
                                   CR_SEEK_CUR, a_offset, a_byte);
}


cr_tknzr_parse_name (CRTknzr * a_this,
                     CRString ** a_str)
{
        guint32 tmp_char = 0;
        CRInputPos init_pos;
        enum CRStatus status = CR_OK;
        gboolean str_needs_free = FALSE,
                is_first_nmchar=TRUE ;
        glong i = 0;
        CRParsingLocation loc = {0} ;

        g_return_val_if_fail (a_this && PRIVATE (a_this)
                              && PRIVATE (a_this)->input
                              && a_str,
                              CR_BAD_PARAM_ERROR) ;

        RECORD_INITIAL_POS (a_this, &init_pos);

        if (*a_str == NULL) {
                *a_str = cr_string_new ();
                str_needs_free = TRUE;
        }
        for (i = 0;; i++) {
                if (is_first_nmchar == TRUE) {
                        status = cr_tknzr_parse_nmchar
                                (a_this, &tmp_char,
                                 &loc) ;
                        is_first_nmchar = FALSE ;
                } else {
                        status = cr_tknzr_parse_nmchar
                                (a_this, &tmp_char, NULL) ;
                }
                if (status != CR_OK)
                        break;
                g_string_append_unichar ((*a_str)->stryng,
                                         tmp_char);
        }
        if (i > 0) {
                cr_parsing_location_copy
                        (&(*a_str)->location, &loc) ;
                return CR_OK;
        }
        if (str_needs_free == TRUE && *a_str) {
                cr_string_destroy (*a_str);
                *a_str = NULL;
        }
        cr_tknzr_set_cur_pos (a_this, &init_pos);
        return CR_PARSING_ERROR;
}


int ext4_get_projid(struct inode *inode, kprojid_t *projid)
{
	if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
		return -EOPNOTSUPP;
	*projid = EXT4_I(inode)->i_projid;
	return 0;
}


int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk,
		       ext4_lblk_t len)
{
	int ret;

	if (ext4_encrypted_inode(inode))
		return ext4_encrypted_zeroout(inode, lblk, pblk, len);

	ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS);
	if (ret > 0)
		ret = 0;

	return ret;
}


static int ext4_da_write_end(struct file *file,
			     struct address_space *mapping,
			     loff_t pos, unsigned len, unsigned copied,
			     struct page *page, void *fsdata)
{
	struct inode *inode = mapping->host;
	int ret = 0, ret2;
	handle_t *handle = ext4_journal_current_handle();
	loff_t new_i_size;
	unsigned long start, end;
	int write_mode = (int)(unsigned long)fsdata;

	if (write_mode == FALL_BACK_TO_NONDELALLOC)
		return ext4_write_end(file, mapping, pos,
				      len, copied, page, fsdata);

	trace_ext4_da_write_end(inode, pos, len, copied);
	start = pos & (PAGE_SIZE - 1);
	end = start + copied - 1;

	/*
	 * generic_write_end() will run mark_inode_dirty() if i_size
	 * changes.  So let's piggyback the i_disksize mark_inode_dirty
	 * into that.
	 */
	new_i_size = pos + copied;
	if (copied && new_i_size > EXT4_I(inode)->i_disksize) {
		if (ext4_has_inline_data(inode) ||
		    ext4_da_should_update_i_disksize(page, end)) {
			ext4_update_i_disksize(inode, new_i_size);
			/* We need to mark inode dirty even if
			 * new_i_size is less that inode->i_size
			 * bu greater than i_disksize.(hint delalloc)
			 */
			ext4_mark_inode_dirty(handle, inode);
		}
	}

	if (write_mode != CONVERT_INLINE_DATA &&
	    ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA) &&
	    ext4_has_inline_data(inode))
		ret2 = ext4_da_write_inline_data_end(inode, pos, len, copied,
						     page);
	else
		ret2 = generic_write_end(file, mapping, pos, len, copied,
							page, fsdata);

	copied = ret2;
	if (ret2 < 0)
		ret = ret2;
	ret2 = ext4_journal_stop(handle);
	if (!ret)
		ret = ret2;

	return ret ? ret : copied;
}


static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
				  get_block_t *get_block)
{
	unsigned from = pos & (PAGE_SIZE - 1);
	unsigned to = from + len;
	struct inode *inode = page->mapping->host;
	unsigned block_start, block_end;
	sector_t block;
	int err = 0;
	unsigned blocksize = inode->i_sb->s_blocksize;
	unsigned bbits;
	struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
	bool decrypt = false;

	BUG_ON(!PageLocked(page));
	BUG_ON(from > PAGE_SIZE);
	BUG_ON(to > PAGE_SIZE);
	BUG_ON(from > to);

	if (!page_has_buffers(page))
		create_empty_buffers(page, blocksize, 0);
	head = page_buffers(page);
	bbits = ilog2(blocksize);
	block = (sector_t)page->index << (PAGE_SHIFT - bbits);

	for (bh = head, block_start = 0; bh != head || !block_start;
	    block++, block_start = block_end, bh = bh->b_this_page) {
		block_end = block_start + blocksize;
		if (block_end <= from || block_start >= to) {
			if (PageUptodate(page)) {
				if (!buffer_uptodate(bh))
					set_buffer_uptodate(bh);
			}
			continue;
		}
		if (buffer_new(bh))
			clear_buffer_new(bh);
		if (!buffer_mapped(bh)) {
			WARN_ON(bh->b_size != blocksize);
			err = get_block(inode, block, bh, 1);
			if (err)
				break;
			if (buffer_new(bh)) {
				unmap_underlying_metadata(bh->b_bdev,
							  bh->b_blocknr);
				if (PageUptodate(page)) {
					clear_buffer_new(bh);
					set_buffer_uptodate(bh);
					mark_buffer_dirty(bh);
					continue;
				}
				if (block_end > to || block_start < from)
					zero_user_segments(page, to, block_end,
							   block_start, from);
				continue;
			}
		}
		if (PageUptodate(page)) {
			if (!buffer_uptodate(bh))
				set_buffer_uptodate(bh);
			continue;
		}
		if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
		    !buffer_unwritten(bh) &&
		    (block_start < from || block_end > to)) {
			ll_rw_block(READ, 1, &bh);
			*wait_bh++ = bh;
			decrypt = ext4_encrypted_inode(inode) &&
				S_ISREG(inode->i_mode);
		}
	}
	/*
	 * If we issued read requests, let them complete.
	 */
	while (wait_bh > wait) {
		wait_on_buffer(*--wait_bh);
		if (!buffer_uptodate(*wait_bh))
			err = -EIO;
	}
	if (unlikely(err))
		page_zero_new_buffers(page, from, to);
	else if (decrypt)
		err = ext4_decrypt(page);
	return err;
}


int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
{
	struct super_block *sb = inode->i_sb;
	ext4_lblk_t first_block, stop_block;
	struct address_space *mapping = inode->i_mapping;
	loff_t first_block_offset, last_block_offset;
	handle_t *handle;
	unsigned int credits;
	int ret = 0;

	if (!S_ISREG(inode->i_mode))
		return -EOPNOTSUPP;

	trace_ext4_punch_hole(inode, offset, length, 0);

	/*
	 * Write out all dirty pages to avoid race conditions
	 * Then release them.
	 */
	if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
		ret = filemap_write_and_wait_range(mapping, offset,
						   offset + length - 1);
		if (ret)
			return ret;
	}

	inode_lock(inode);

	/* No need to punch hole beyond i_size */
	if (offset >= inode->i_size)
		goto out_mutex;

	/*
	 * If the hole extends beyond i_size, set the hole
	 * to end after the page that contains i_size
	 */
	if (offset + length > inode->i_size) {
		length = inode->i_size +
		   PAGE_SIZE - (inode->i_size & (PAGE_SIZE - 1)) -
		   offset;
	}

	if (offset & (sb->s_blocksize - 1) ||
	    (offset + length) & (sb->s_blocksize - 1)) {
		/*
		 * Attach jinode to inode for jbd2 if we do any zeroing of
		 * partial block
		 */
		ret = ext4_inode_attach_jinode(inode);
		if (ret < 0)
			goto out_mutex;

	}

	/* Wait all existing dio workers, newcomers will block on i_mutex */
	ext4_inode_block_unlocked_dio(inode);
	inode_dio_wait(inode);

	/*
	 * Prevent page faults from reinstantiating pages we have released from
	 * page cache.
	 */
	down_write(&EXT4_I(inode)->i_mmap_sem);
	first_block_offset = round_up(offset, sb->s_blocksize);
	last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;

	/* Now release the pages and zero block aligned part of pages*/
	if (last_block_offset > first_block_offset) {
		ret = ext4_update_disksize_before_punch(inode, offset, length);
		if (ret)
			goto out_dio;
		truncate_pagecache_range(inode, first_block_offset,
					 last_block_offset);
	}

	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
		credits = ext4_writepage_trans_blocks(inode);
	else
		credits = ext4_blocks_for_truncate(inode);
	handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits);
	if (IS_ERR(handle)) {
		ret = PTR_ERR(handle);
		ext4_std_error(sb, ret);
		goto out_dio;
	}

	ret = ext4_zero_partial_blocks(handle, inode, offset,
				       length);
	if (ret)
		goto out_stop;

	first_block = (offset + sb->s_blocksize - 1) >>
		EXT4_BLOCK_SIZE_BITS(sb);
	stop_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);

	/* If there are no blocks to remove, return now */
	if (first_block >= stop_block)
		goto out_stop;

	down_write(&EXT4_I(inode)->i_data_sem);
	ext4_discard_preallocations(inode);

	ret = ext4_es_remove_extent(inode, first_block,
				    stop_block - first_block);
	if (ret) {
		up_write(&EXT4_I(inode)->i_data_sem);
		goto out_stop;
	}

	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
		ret = ext4_ext_remove_space(inode, first_block,
					    stop_block - 1);
	else
		ret = ext4_ind_remove_space(handle, inode, first_block,
					    stop_block);

	up_write(&EXT4_I(inode)->i_data_sem);
	if (IS_SYNC(inode))
		ext4_handle_sync(handle);

	inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
	ext4_mark_inode_dirty(handle, inode);
out_stop:
	ext4_journal_stop(handle);
out_dio:
	up_write(&EXT4_I(inode)->i_mmap_sem);
	ext4_inode_resume_unlocked_dio(inode);
out_mutex:
	inode_unlock(inode);
	return ret;
}


static void ext4_update_bh_state(struct buffer_head *bh, unsigned long flags)
{
	unsigned long old_state;
	unsigned long new_state;

	flags &= EXT4_MAP_FLAGS;

	/* Dummy buffer_head? Set non-atomically. */
	if (!bh->b_page) {
		bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | flags;
		return;
	}
	/*
	 * Someone else may be modifying b_state. Be careful! This is ugly but
	 * once we get rid of using bh as a container for mapping information
	 * to pass to / from get_block functions, this can go away.
	 */
	do {
		old_state = READ_ONCE(bh->b_state);
		new_state = (old_state & ~EXT4_MAP_FLAGS) | flags;
	} while (unlikely(
		 cmpxchg(&bh->b_state, old_state, new_state) != old_state));
}


static int fsmMknod(const char *path, mode_t mode, dev_t dev)
{
    /* FIX: check S_IFIFO or dev != 0 */
    int rc = mknod(path, (mode & ~07777), dev);

    if (_fsm_debug) {
	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n",
	       __func__, path, (unsigned)(mode & ~07777),
	       (unsigned)dev, (rc < 0 ? strerror(errno) : ""));
    }

    if (rc < 0)
	rc = RPMERR_MKNOD_FAILED;

    return rc;
}


static DNLI_t dnlFreeIterator(DNLI_t dnli)
{
    if (dnli) {
	if (dnli->active) free(dnli->active);
	free(dnli);
    }
    return NULL;
}


static int fsmSetFCaps(const char *path, const char *captxt)
{
    int rc = 0;
#if WITH_CAP
    if (captxt && *captxt != '\0') {
	cap_t fcaps = cap_from_text(captxt);
	if (fcaps == NULL || cap_set_file(path, fcaps) != 0) {
	    rc = RPMERR_SETCAP_FAILED;
	}
	cap_free(fcaps);
    }
#endif
    return rc;
}


static int rpmVerifyScript(rpmts ts, Header h)
{
    int rc = 0;

    if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
	/* fake up a erasure transaction element */
	rpmte p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);

	if (p != NULL) {
	    rpmteSetHeader(p, h);

	    rc = (rpmpsmRun(ts, p, PKG_VERIFY) != RPMRC_OK);

	    /* clean up our fake transaction bits */
	    rpmteFree(p);
	} else {
	    rc = RPMRC_FAIL;
	}
    }

    return rc;
}


void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req)
{
	if (reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req)) {
		reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
		reqsk_put(req);
	}
}


static void reqsk_timer_handler(unsigned long data)
{
	struct request_sock *req = (struct request_sock *)data;
	struct sock *sk_listener = req->rsk_listener;
	struct net *net = sock_net(sk_listener);
	struct inet_connection_sock *icsk = inet_csk(sk_listener);
	struct request_sock_queue *queue = &icsk->icsk_accept_queue;
	int qlen, expire = 0, resend = 0;
	int max_retries, thresh;
	u8 defer_accept;

	if (sk_state_load(sk_listener) != TCP_LISTEN)
		goto drop;

	max_retries = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_synack_retries;
	thresh = max_retries;
	/* Normally all the openreqs are young and become mature
	 * (i.e. converted to established socket) for first timeout.
	 * If synack was not acknowledged for 1 second, it means
	 * one of the following things: synack was lost, ack was lost,
	 * rtt is high or nobody planned to ack (i.e. synflood).
	 * When server is a bit loaded, queue is populated with old
	 * open requests, reducing effective size of queue.
	 * When server is well loaded, queue size reduces to zero
	 * after several minutes of work. It is not synflood,
	 * it is normal operation. The solution is pruning
	 * too old entries overriding normal timeout, when
	 * situation becomes dangerous.
	 *
	 * Essentially, we reserve half of room for young
	 * embrions; and abort old ones without pity, if old
	 * ones are about to clog our table.
	 */
	qlen = reqsk_queue_len(queue);
	if ((qlen << 1) > max(8U, sk_listener->sk_max_ack_backlog)) {
		int young = reqsk_queue_len_young(queue) << 1;

		while (thresh > 2) {
			if (qlen < young)
				break;
			thresh--;
			young <<= 1;
		}
	}
	defer_accept = READ_ONCE(queue->rskq_defer_accept);
	if (defer_accept)
		max_retries = defer_accept;
	syn_ack_recalc(req, thresh, max_retries, defer_accept,
		       &expire, &resend);
	req->rsk_ops->syn_ack_timeout(req);
	if (!expire &&
	    (!resend ||
	     !inet_rtx_syn_ack(sk_listener, req) ||
	     inet_rsk(req)->acked)) {
		unsigned long timeo;

		if (req->num_timeout++ == 0)
			atomic_dec(&queue->young);
		timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
		mod_timer(&req->rsk_timer, jiffies + timeo);
		return;
	}
drop:
	inet_csk_reqsk_queue_drop_and_put(sk_listener, req);
}


void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
				   unsigned long timeout)
{
	reqsk_queue_hash_req(req, timeout);
	inet_csk_reqsk_queue_added(sk);
}


struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
					 struct request_sock *req, bool own_req)
{
	if (own_req) {
		inet_csk_reqsk_queue_drop(sk, req);
		reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
		if (inet_csk_reqsk_queue_add(sk, req, child))
			return child;
	}
	/* Too bad, another child took ownership of the request, undo. */
	bh_unlock_sock(child);
	sock_put(child);
	return NULL;
}


static void __exit ipsec_pfkey_exit(void)
{
	xfrm_unregister_km(&pfkeyv2_mgr);
	sock_unregister(PF_KEY);
	unregister_pernet_subsys(&pfkey_net_ops);
	proto_unregister(&key_proto);
}


static inline void pfkey_hdr_dup(struct sadb_msg *new,
				 const struct sadb_msg *orig)
{
	*new = *orig;
}


static void __net_exit pfkey_net_exit(struct net *net)
{
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);

	pfkey_exit_proc(net);
	BUG_ON(!hlist_empty(&net_pfkey->table));
}


int pfkey_sockaddr_extract(const struct sockaddr *sa, xfrm_address_t *xaddr)
{
	switch (sa->sa_family) {
	case AF_INET:
		xaddr->a4 =
			((struct sockaddr_in *)sa)->sin_addr.s_addr;
		return AF_INET;
#if IS_ENABLED(CONFIG_IPV6)
	case AF_INET6:
		memcpy(xaddr->a6,
		       &((struct sockaddr_in6 *)sa)->sin6_addr,
		       sizeof(struct in6_addr));
		return AF_INET6;
#endif
	}
	return 0;
}


static int verify_address_len(const void *p)
{
	const struct sadb_address *sp = p;
	const struct sockaddr *addr = (const struct sockaddr *)(sp + 1);
	const struct sockaddr_in *sin;
#if IS_ENABLED(CONFIG_IPV6)
	const struct sockaddr_in6 *sin6;
#endif
	int len;

	switch (addr->sa_family) {
	case AF_INET:
		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));
		if (sp->sadb_address_len != len ||
		    sp->sadb_address_prefixlen > 32)
			return -EINVAL;
		break;
#if IS_ENABLED(CONFIG_IPV6)
	case AF_INET6:
		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t));
		if (sp->sadb_address_len != len ||
		    sp->sadb_address_prefixlen > 128)
			return -EINVAL;
		break;
#endif
	default:
		/* It is user using kernel to keep track of security
		 * associations for another protocol, such as
		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify
		 * lengths.
		 *
		 * XXX Actually, association/policy database is not yet
		 * XXX able to cope with arbitrary sockaddr families.
		 * XXX When it can, remove this -EINVAL.  -DaveM
		 */
		return -EINVAL;
	}

	return 0;
}


static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
	struct pfkey_sock *pfk = pfkey_sk(sk);
	int satype = hdr->sadb_msg_satype;
	bool reset_errno = false;

	if (hdr->sadb_msg_len == (sizeof(*hdr) / sizeof(uint64_t))) {
		reset_errno = true;
		if (satype != 0 && satype != 1)
			return -EINVAL;
		pfk->promisc = satype;
	}
	if (reset_errno && skb_cloned(skb))
		skb = skb_copy(skb, GFP_KERNEL);
	else
		skb = skb_clone(skb, GFP_KERNEL);

	if (reset_errno && skb) {
		struct sadb_msg *new_hdr = (struct sadb_msg *) skb->data;
		new_hdr->sadb_msg_errno = 0;
	}

	pfkey_broadcast(skb, BROADCAST_ALL, NULL, sock_net(sk));
	return 0;
}


static bool pfkey_is_alive(const struct km_event *c)
{
	struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
	struct sock *sk;
	bool is_alive = false;

	rcu_read_lock();
	sk_for_each_rcu(sk, &net_pfkey->table) {
		if (pfkey_sk(sk)->registered) {
			is_alive = true;
			break;
		}
	}
	rcu_read_unlock();

	return is_alive;
}


static int __net_init pfkey_init_proc(struct net *net)
{
	struct proc_dir_entry *e;

	e = proc_create("pfkey", 0, net->proc_net, &pfkey_proc_ops);
	if (e == NULL)
		return -ENOMEM;

	return 0;
}


static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
{
	struct net *net = x ? xs_net(x) : c->net;
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);

	if (atomic_read(&net_pfkey->socks_nr) == 0)
		return 0;

	switch (c->event) {
	case XFRM_MSG_EXPIRE:
		return key_notify_sa_expire(x, c);
	case XFRM_MSG_DELSA:
	case XFRM_MSG_NEWSA:
	case XFRM_MSG_UPDSA:
		return key_notify_sa(x, c);
	case XFRM_MSG_FLUSHSA:
		return key_notify_sa_flush(c);
	case XFRM_MSG_NEWAE: /* not yet supported */
		break;
	default:
		pr_err("pfkey: Unknown SA event %d\n", c->event);
		break;
	}

	return 0;
}


static u32 gen_reqid(struct net *net)
{
	struct xfrm_policy_walk walk;
	u32 start;
	int rc;
	static u32 reqid = IPSEC_MANUAL_REQID_MAX;

	start = reqid;
	do {
		++reqid;
		if (reqid == 0)
			reqid = IPSEC_MANUAL_REQID_MAX+1;
		xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
		rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid);
		xfrm_policy_walk_done(&walk, net);
		if (rc != -EEXIST)
			return reqid;
	} while (reqid != start);
	return 0;
}


static int pfkey_dump_sp(struct pfkey_sock *pfk)
{
	struct net *net = sock_net(&pfk->sk);
	return xfrm_policy_walk(net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
}


static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
					      int add_keys, int hsc)
{
	struct sk_buff *skb;
	struct sadb_msg *hdr;
	struct sadb_sa *sa;
	struct sadb_lifetime *lifetime;
	struct sadb_address *addr;
	struct sadb_key *key;
	struct sadb_x_sa2 *sa2;
	struct sadb_x_sec_ctx *sec_ctx;
	struct xfrm_sec_ctx *xfrm_ctx;
	int ctx_size = 0;
	int size;
	int auth_key_size = 0;
	int encrypt_key_size = 0;
	int sockaddr_size;
	struct xfrm_encap_tmpl *natt = NULL;
	int mode;

	/* address family check */
	sockaddr_size = pfkey_sockaddr_size(x->props.family);
	if (!sockaddr_size)
		return ERR_PTR(-EINVAL);

	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
	   key(AE), (identity(SD),) (sensitivity)> */
	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
		sizeof(struct sadb_lifetime) +
		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
			sizeof(struct sadb_address)*2 +
				sockaddr_size*2 +
					sizeof(struct sadb_x_sa2);

	if ((xfrm_ctx = x->security)) {
		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
	}

	/* identity & sensitivity */
	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family))
		size += sizeof(struct sadb_address) + sockaddr_size;

	if (add_keys) {
		if (x->aalg && x->aalg->alg_key_len) {
			auth_key_size =
				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
			size += sizeof(struct sadb_key) + auth_key_size;
		}
		if (x->ealg && x->ealg->alg_key_len) {
			encrypt_key_size =
				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
			size += sizeof(struct sadb_key) + encrypt_key_size;
		}
	}
	if (x->encap)
		natt = x->encap;

	if (natt && natt->encap_type) {
		size += sizeof(struct sadb_x_nat_t_type);
		size += sizeof(struct sadb_x_nat_t_port);
		size += sizeof(struct sadb_x_nat_t_port);
	}

	skb =  alloc_skb(size + 16, GFP_ATOMIC);
	if (skb == NULL)
		return ERR_PTR(-ENOBUFS);

	/* call should fill header later */
	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
	memset(hdr, 0, size);	/* XXX do we need this ? */
	hdr->sadb_msg_len = size / sizeof(uint64_t);

	/* sa */
	sa = (struct sadb_sa *)  skb_put(skb, sizeof(struct sadb_sa));
	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
	sa->sadb_sa_exttype = SADB_EXT_SA;
	sa->sadb_sa_spi = x->id.spi;
	sa->sadb_sa_replay = x->props.replay_window;
	switch (x->km.state) {
	case XFRM_STATE_VALID:
		sa->sadb_sa_state = x->km.dying ?
			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
		break;
	case XFRM_STATE_ACQ:
		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
		break;
	default:
		sa->sadb_sa_state = SADB_SASTATE_DEAD;
		break;
	}
	sa->sadb_sa_auth = 0;
	if (x->aalg) {
		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
		sa->sadb_sa_auth = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	sa->sadb_sa_encrypt = 0;
	BUG_ON(x->ealg && x->calg);
	if (x->ealg) {
		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
	if (x->calg) {
		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}

	sa->sadb_sa_flags = 0;
	if (x->props.flags & XFRM_STATE_NOECN)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;

	/* hard time */
	if (hsc & 2) {
		lifetime = (struct sadb_lifetime *)  skb_put(skb,
							     sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
	}
	/* soft time */
	if (hsc & 1) {
		lifetime = (struct sadb_lifetime *)  skb_put(skb,
							     sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
	}
	/* current time */
	lifetime = (struct sadb_lifetime *)  skb_put(skb,
						     sizeof(struct sadb_lifetime));
	lifetime->sadb_lifetime_len =
		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
	lifetime->sadb_lifetime_allocations = x->curlft.packets;
	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
	/* src address */
	addr = (struct sadb_address*) skb_put(skb,
					      sizeof(struct sadb_address)+sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	/* "if the ports are non-zero, then the sadb_address_proto field,
	   normally zero, MUST be filled in with the transport
	   protocol's number." - RFC2367 */
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->props.saddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	if (!addr->sadb_address_prefixlen)
		BUG();

	/* dst address */
	addr = (struct sadb_address*) skb_put(skb,
					      sizeof(struct sadb_address)+sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->id.daddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	if (!addr->sadb_address_prefixlen)
		BUG();

	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr,
			     x->props.family)) {
		addr = (struct sadb_address*) skb_put(skb,
			sizeof(struct sadb_address)+sockaddr_size);
		addr->sadb_address_len =
			(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
		addr->sadb_address_proto =
			pfkey_proto_from_xfrm(x->sel.proto);
		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
		addr->sadb_address_reserved = 0;

		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	}

	/* auth key */
	if (add_keys && auth_key_size) {
		key = (struct sadb_key *) skb_put(skb,
						  sizeof(struct sadb_key)+auth_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
			sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
		key->sadb_key_bits = x->aalg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
	}
	/* encrypt key */
	if (add_keys && encrypt_key_size) {
		key = (struct sadb_key *) skb_put(skb,
						  sizeof(struct sadb_key)+encrypt_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) +
				     encrypt_key_size) / sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
		key->sadb_key_bits = x->ealg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->ealg->alg_key,
		       (x->ealg->alg_key_len+7)/8);
	}

	/* sa */
	sa2 = (struct sadb_x_sa2 *)  skb_put(skb, sizeof(struct sadb_x_sa2));
	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
		kfree_skb(skb);
		return ERR_PTR(-EINVAL);
	}
	sa2->sadb_x_sa2_mode = mode;
	sa2->sadb_x_sa2_reserved1 = 0;
	sa2->sadb_x_sa2_reserved2 = 0;
	sa2->sadb_x_sa2_sequence = 0;
	sa2->sadb_x_sa2_reqid = x->props.reqid;

	if (natt && natt->encap_type) {
		struct sadb_x_nat_t_type *n_type;
		struct sadb_x_nat_t_port *n_port;

		/* type */
		n_type = (struct sadb_x_nat_t_type*) skb_put(skb, sizeof(*n_type));
		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
		n_type->sadb_x_nat_t_type_type = natt->encap_type;
		n_type->sadb_x_nat_t_type_reserved[0] = 0;
		n_type->sadb_x_nat_t_type_reserved[1] = 0;
		n_type->sadb_x_nat_t_type_reserved[2] = 0;

		/* source port */
		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
		n_port->sadb_x_nat_t_port_reserved = 0;

		/* dest port */
		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
		n_port->sadb_x_nat_t_port_reserved = 0;
	}

	/* security context */
	if (xfrm_ctx) {
		sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
				sizeof(struct sadb_x_sec_ctx) + ctx_size);
		sec_ctx->sadb_x_sec_len =
		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
		       xfrm_ctx->ctx_len);
	}

	return skb;
}


static int key_notify_policy_flush(const struct km_event *c)
{
	struct sk_buff *skb_out;
	struct sadb_msg *hdr;

	skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
	if (!skb_out)
		return -ENOBUFS;
	hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
	hdr->sadb_msg_type = SADB_X_SPDFLUSH;
	hdr->sadb_msg_seq = c->seq;
	hdr->sadb_msg_pid = c->portid;
	hdr->sadb_msg_version = PF_KEY_V2;
	hdr->sadb_msg_errno = (uint8_t) 0;
	hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
	hdr->sadb_msg_reserved = 0;
	pfkey_broadcast(skb_out, BROADCAST_ALL, NULL, c->net);
	return 0;

}


static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
			      const struct xfrm_migrate *m, int num_bundles,
			      const struct xfrm_kmaddress *k)
{
	return -ENOPROTOOPT;
}


static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
	struct net *net = sock_net(sk);
	struct sk_buff *resp_skb;
	struct sadb_x_sa2 *sa2;
	struct sadb_address *saddr, *daddr;
	struct sadb_msg *out_hdr;
	struct sadb_spirange *range;
	struct xfrm_state *x = NULL;
	int mode;
	int err;
	u32 min_spi, max_spi;
	u32 reqid;
	u8 proto;
	unsigned short family;
	xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;

	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
		return -EINVAL;

	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
	if (proto == 0)
		return -EINVAL;

	if ((sa2 = ext_hdrs[SADB_X_EXT_SA2-1]) != NULL) {
		mode = pfkey_mode_to_xfrm(sa2->sadb_x_sa2_mode);
		if (mode < 0)
			return -EINVAL;
		reqid = sa2->sadb_x_sa2_reqid;
	} else {
		mode = 0;
		reqid = 0;
	}

	saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
	daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];

	family = ((struct sockaddr *)(saddr + 1))->sa_family;
	switch (family) {
	case AF_INET:
		xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
		xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
		break;
#if IS_ENABLED(CONFIG_IPV6)
	case AF_INET6:
		xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
		xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
		break;
#endif
	}

	if (hdr->sadb_msg_seq) {
		x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
		if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) {
			xfrm_state_put(x);
			x = NULL;
		}
	}

	if (!x)
		x = xfrm_find_acq(net, &dummy_mark, mode, reqid, proto, xdaddr, xsaddr, 1, family);

	if (x == NULL)
		return -ENOENT;

	min_spi = 0x100;
	max_spi = 0x0fffffff;

	range = ext_hdrs[SADB_EXT_SPIRANGE-1];
	if (range) {
		min_spi = range->sadb_spirange_min;
		max_spi = range->sadb_spirange_max;
	}

	err = verify_spi_info(x->id.proto, min_spi, max_spi);
	if (err) {
		xfrm_state_put(x);
		return err;
	}

	err = xfrm_alloc_spi(x, min_spi, max_spi);
	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x);

	if (IS_ERR(resp_skb)) {
		xfrm_state_put(x);
		return  PTR_ERR(resp_skb);
	}

	out_hdr = (struct sadb_msg *) resp_skb->data;
	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
	out_hdr->sadb_msg_type = SADB_GETSPI;
	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
	out_hdr->sadb_msg_errno = 0;
	out_hdr->sadb_msg_reserved = 0;
	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;

	xfrm_state_put(x);

	pfkey_broadcast(resp_skb, BROADCAST_ONE, sk, net);

	return 0;
}


static void pfkey_insert(struct sock *sk)
{
	struct net *net = sock_net(sk);
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);

	mutex_lock(&pfkey_mutex);
	sk_add_node_rcu(sk, &net_pfkey->table);
	mutex_unlock(&pfkey_mutex);
}


static char *_unescape_lf(char *str)
{
    char *c, *p = str;
    gsize len = 0;

    while ((c = strchr(p, '\\')) != NULL)
    {
        if (p != &str[len])
            memmove(&str[len], p, c - p);
        len += (c - p);
        if (c[1] == 'n')
        {
            str[len++] = '\n';
            c++;
        }
        else if (c != &str[len])
            str[len++] = *c;
        p = &c[1];
    }
    if (p != &str[len])
        memmove(&str[len], p, strlen(p) + 1);
    return str;
}


static gboolean read_all_used_files( FILE* f, int* n_files, char*** used_files )
{
    char line[ 4096 ];
    int i, n, x;
    char** files;
    int ver_maj, ver_min;

    /* the first line of the cache file is version number */
    if( !fgets(line, G_N_ELEMENTS(line),f) )
        return FALSE;
    if( sscanf(line, "%d.%d", &ver_maj, &ver_min)< 2 )
        return FALSE;
    if (ver_maj != VER_MAJOR ||
        ver_min > VER_MINOR || ver_min < VER_MINOR_SUPPORTED)
        return FALSE;

    /* skip the second line containing menu name */
    if( ! fgets( line, G_N_ELEMENTS(line), f ) )
        return FALSE;

    /* num of files used */
    if( ! fgets( line, G_N_ELEMENTS(line), f ) )
        return FALSE;

    n = atoi( line );
    files = g_new0( char*, n + 1 );

    for( i = 0, x = 0; i < n; ++i )
    {
        int len;

        if( ! fgets( line, G_N_ELEMENTS(line), f ) )
            return FALSE;

        len = strlen( line );
        if( len <= 1 )
            return FALSE;
        files[ x ] = g_strndup( line, len - 1 ); /* don't include \n */
        if (g_file_test(files[x]+1, G_FILE_TEST_EXISTS))
            x++;
        else
        {
            DEBUG("ignoring not existent file from menu-cache-gen: %s", files[x]);
            g_free(files[x]);
            files[x] = NULL;
        }
    }
    *n_files = x;
    *used_files = files;
    return TRUE;
}


static gboolean on_new_conn_incoming(GIOChannel* ch, GIOCondition cond, gpointer user_data)
{
    int server, client;
    socklen_t client_addrlen;
    struct sockaddr client_addr;
    GIOChannel* child;
    ClientIO* client_io;

    server = g_io_channel_unix_get_fd(ch);

    client_addrlen = sizeof(client_addr);
    client = accept(server, &client_addr, &client_addrlen);
    if( client == -1 )
    {
        DEBUG("accept error");
        return TRUE;
    }

    child = g_io_channel_unix_new(client);
    g_io_channel_set_close_on_unref( child, TRUE );

    client_io = g_new0 (ClientIO, 1);
    client_io->channel = child;
    client_io->source_id =
        g_io_add_watch(child, G_IO_PRI|G_IO_IN|G_IO_HUP|G_IO_ERR,
                        on_client_data_in, client_io);
    g_io_channel_unref(child);

    DEBUG("new client accepted: %p", child);
    return TRUE;
}


static gpointer server_io_thread(gpointer data)
{
    char buf[1024]; /* protocol has a lot shorter strings */
    ssize_t sz;
    size_t ptr = 0;
    int fd = GPOINTER_TO_INT(data);
    GHashTableIter it;
    char* menu_name;
    MenuCache* cache;

    while(fd >= 0)
    {
        sz = read(fd, &buf[ptr], sizeof(buf) - ptr);
        if(sz <= 0) /* socket error or EOF */
        {
            MENU_CACHE_LOCK;
            ptr = hash ? g_hash_table_size(hash) : 0;
            MENU_CACHE_UNLOCK;
            if (ptr == 0) /* don't need it anymore */
                break;
            G_LOCK(connect);
            if(fd != server_fd) /* someone replaced us?! go out immediately! */
            {
                G_UNLOCK(connect);
                break;
            }
            server_fd = -1;
            G_UNLOCK(connect);
            DEBUG("connect failed, trying reconnect");
            sleep(1);
            if( ! connect_server(NULL) )
            {
                g_critical("fail to re-connect to the server.");
                MENU_CACHE_LOCK;
                if(hash)
                {
                    g_hash_table_iter_init(&it, hash);
                    while(g_hash_table_iter_next(&it, (gpointer*)&menu_name, (gpointer*)&cache))
                        SET_CACHE_READY(cache);
                }
                MENU_CACHE_UNLOCK;
                break;
            }
            DEBUG("successfully reconnected server, re-register menus.");
            /* re-register all menu caches */
            MENU_CACHE_LOCK;
            if(hash)
            {
                g_hash_table_iter_init(&it, hash);
                while(g_hash_table_iter_next(&it, (gpointer*)&menu_name, (gpointer*)&cache))
                    register_menu_to_server(cache);
                    /* FIXME: need we remove it from hash if failed? */
            }
            MENU_CACHE_UNLOCK;
            break; /* next thread will do it */
        }
        while(sz > 0)
        {
            while(sz > 0)
            {
                if(buf[ptr] == '\n')
                    break;
                sz--;
                ptr++;
            }
            if(ptr == sizeof(buf)) /* EOB reached, seems we got garbage */
            {
                g_warning("menu cache: got garbage from server, break connect");
                shutdown(fd, SHUT_RDWR); /* drop connection */
                break; /* we handle it above */
            }
            else if(sz == 0) /* incomplete line, wait for data again */
                break;
            /* we got a line, let check what we got */
            buf[ptr] = '\0';
            if(memcmp(buf, "REL:", 4) == 0) /* reload */
            {
                DEBUG("server ask us to reload cache: %s", &buf[4]);
                MENU_CACHE_LOCK;
                if(hash)
                {
                    g_hash_table_iter_init(&it, hash);
                    while(g_hash_table_iter_next(&it, (gpointer*)&menu_name, (gpointer*)&cache))
                    {
                        if(memcmp(cache->md5, &buf[4], 32) == 0)
                        {
                            DEBUG("RELOAD!");
                            menu_cache_reload(cache);
                            SET_CACHE_READY(cache);
                            break;
                        }
                    }
                }
                MENU_CACHE_UNLOCK;
                /* DEBUG("cache reloaded"); */
            }
            else
                g_warning("menu cache: unrecognized input: %s", buf);
            /* go to next line */
            sz--;
            if(sz > 0)
                memmove(buf, &buf[ptr+1], sz);
            ptr = 0;
        }
    }
    G_LOCK(connect);
    if (fd == server_fd)
        server_fd = -1;
    G_UNLOCK(connect);
    close(fd);
    /* DEBUG("server io thread terminated"); */
#if GLIB_CHECK_VERSION(2, 32, 0)
    g_thread_unref(g_thread_self());
#endif
    return NULL;
}


char* menu_cache_dir_make_path( MenuCacheDir* dir )
{
    GString* path = g_string_sized_new(1024);
    MenuCacheItem* it;

    MENU_CACHE_LOCK;
    while( (it = MENU_CACHE_ITEM(dir)) ) /* this is not top dir */
    {
        g_string_prepend( path, menu_cache_item_get_id(it) );
        g_string_prepend_c( path, '/' );
        /* FIXME: if parent is already unref'd by another thread then
           path being made will be broken. Is there any way to avoid that? */
        dir = it->parent;
    }
    MENU_CACHE_UNLOCK;
    return g_string_free( path, FALSE );
}


static gboolean on_client_data_in(GIOChannel* ch, GIOCondition cond, gpointer user_data)
{
    char *line;
    gsize len;
    GIOStatus st;
    const char* md5;
    Cache* cache;
    GFile* gf;
    gboolean ret = TRUE;

    if(cond & (G_IO_HUP|G_IO_ERR) )
    {
        on_client_closed(user_data);
        return FALSE; /* remove the watch */
    }

retry:
    st = g_io_channel_read_line( ch, &line, &len, NULL, NULL );
    if( st == G_IO_STATUS_AGAIN )
        goto retry;
    if( st != G_IO_STATUS_NORMAL ) {
        on_client_closed(user_data);
        return FALSE;
    }

    --len;
    line[len] = 0;

    DEBUG("line(%d) = %s", (int)len, line);

    if( memcmp(line, "REG:", 4) == 0 )
    {
        int n_files = 0, i;
        char *pline = line + 4;
        char *sep, *menu_name, *lang_name, *cache_dir;
        char **files = NULL;
        char **env;
        char reload_cmd[38] = "REL:";

        len -= 4;
        /* Format of received string, separated by '\t'.
         * Menu Name
         * Language Name
         * XDG_CACHE_HOME
         * XDG_CONFIG_DIRS
         * XDG_MENU_PREFIX
         * XDG_DATA_DIRS
         * XDG_CONFIG_HOME
         * XDG_DATA_HOME
         * (optional) CACHE_GEN_VERSION
         * md5 hash */

        md5 = pline + len - 32; /* the md5 hash of this menu */

        cache = (Cache*)g_hash_table_lookup(hash, md5);
        if( !cache )
        {
            sep = strchr(pline, '\t');
            *sep = '\0';
            menu_name = pline;
            pline = sep + 1;

            sep = strchr(pline, '\t');
            *sep = '\0';
            lang_name = pline;
            pline = sep + 1;

            ((char *)md5)[-1] = '\0';
            env = g_strsplit(pline, "\t", 0);

            cache_dir = env[0]; /* XDG_CACHE_HOME */
            /* obtain cache dir from client's env */

            cache = g_slice_new0( Cache );
            cache->cache_file = g_build_filename(*cache_dir ? cache_dir : g_get_user_cache_dir(), "menus", md5, NULL );
            if( ! cache_file_is_updated(cache->cache_file, &n_files, &files) )
            {
                /* run menu-cache-gen */
                if(! regenerate_cache( menu_name, lang_name, cache->cache_file, env, &n_files, &files ) )
                {
                    DEBUG("regeneration of cache failed!!");
                }
            }
            else
            {
                /* file loaded, schedule update anyway */
                cache->need_reload = TRUE;
                cache->delayed_reload_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW, 3,
                                                    (GSourceFunc)delayed_reload, cache, NULL);
            }
            memcpy( cache->md5, md5, 33 );
            cache->n_files = n_files;
            cache->files = files;
            cache->menu_name = g_strdup(menu_name);
            cache->lang_name = g_strdup(lang_name);
            cache->env = env;
            cache->mons = g_new0(GFileMonitor*, n_files+1);
            /* create required file monitors */
            DEBUG("%d files/dirs are monitored.", n_files);
            for( i = 0; i < n_files; ++i )
            {
                gf = g_file_new_for_path( files[i] + 1 );
                if( files[i][0] == 'D' )
                    cache->mons[i] = g_file_monitor_directory( gf, 0, NULL, NULL );
                else
                    cache->mons[i] = g_file_monitor_file( gf, 0, NULL, NULL );
                DEBUG("monitor: %s", g_file_get_path(gf));
                g_signal_connect(cache->mons[i], "changed",
                                 G_CALLBACK(on_file_changed), cache);
                g_object_unref(gf);
            }
            /*
            gf = g_file_new_for_path( cache_file );
            cache->cache_mon = g_file_monitor_file( gf, 0, NULL, NULL );
            g_signal_connect( cache->cache_mon, "changed", on_file_changed, cache);
            g_object_unref(gf);
            */
            g_hash_table_insert(hash, cache->md5, cache);
            DEBUG("new menu cache %p added to hash", cache);
        }
        else if (access(cache->cache_file, R_OK) != 0)
        {
            /* bug SF#657: if user deleted cache file we have to regenerate it */
            if (!regenerate_cache(cache->menu_name, cache->lang_name, cache->cache_file,
                                  cache->env, &cache->n_files, &cache->files))
            {
                DEBUG("regeneration of cache failed.");
            }
        }
        /* DEBUG("menu %s requested by client %d", md5, g_io_channel_unix_get_fd(ch)); */
        cache->clients = g_slist_prepend(cache->clients, user_data);
        if(cache->delayed_free_handler)
        {
            g_source_remove(cache->delayed_free_handler);
            cache->delayed_free_handler = 0;
        }
        DEBUG("client %p added to cache %p", ch, cache);

        /* generate a fake reload notification */
        DEBUG("fake reload!");
        memcpy(reload_cmd + 4, md5, 32);

        reload_cmd[36] = '\n';
        reload_cmd[37] = '\0';

        DEBUG("reload command: %s", reload_cmd);
        ret = write(g_io_channel_unix_get_fd(ch), reload_cmd, 37) > 0;
    }
    else if( memcmp(line, "UNR:", 4) == 0 )
    {
        md5 = line + 4;
        DEBUG("unregister: %s", md5);
        cache = (Cache*)g_hash_table_lookup(hash, md5);
        if(cache && cache->clients)
        {
            /* remove the IO channel from the cache */
            cache->clients = g_slist_remove(cache->clients, user_data);
            if(cache->clients == NULL)
                cache_free(cache);
        }
        else
            DEBUG("bug! client is not found.");
    }
    g_free( line );

    return ret;
}


static gboolean delayed_reload( Cache* cache )
{
    if(g_source_is_destroyed(g_main_current_source()))
        return FALSE;

    if(cache->need_reload)
        do_reload(cache);

    if (cache->need_reload)
      return TRUE;

    cache->delayed_reload_handler = 0;
    return FALSE;
}


gboolean menu_cache_app_get_use_sn( MenuCacheApp* app )
{
    return ( (app->flags & FLAG_USE_SN) != 0 );
}


static gpointer menu_cache_loader_thread(gpointer data)
{
    MenuCache* cache = (MenuCache*)data;

    /* try to connect server now */
    if(!connect_server(cache->cancellable))
    {
        g_print("unable to connect to menu-cached.\n");
        SET_CACHE_READY(cache);
        return NULL;
    }
    /* and request update from server */
    if ((cache->cancellable && g_cancellable_is_cancelled(cache->cancellable)) ||
        !register_menu_to_server(cache))
        SET_CACHE_READY(cache);
    return NULL;
}


int yr_object_from_external_variable(
    YR_EXTERNAL_VARIABLE* external,
    YR_OBJECT** object)
{
  YR_OBJECT* obj;
  int result;
  uint8_t obj_type = 0;

  switch(external->type)
  {
    case EXTERNAL_VARIABLE_TYPE_INTEGER:
    case EXTERNAL_VARIABLE_TYPE_BOOLEAN:
      obj_type = OBJECT_TYPE_INTEGER;
      break;

    case EXTERNAL_VARIABLE_TYPE_FLOAT:
      obj_type = OBJECT_TYPE_FLOAT;
      break;

    case EXTERNAL_VARIABLE_TYPE_STRING:
    case EXTERNAL_VARIABLE_TYPE_MALLOC_STRING:
      obj_type = OBJECT_TYPE_STRING;
      break;

    default:
      assert(FALSE);
  }

  result = yr_object_create(
      obj_type,
      external->identifier,
      NULL,
      &obj);

  if (result == ERROR_SUCCESS)
  {
    switch(external->type)
    {
      case EXTERNAL_VARIABLE_TYPE_INTEGER:
      case EXTERNAL_VARIABLE_TYPE_BOOLEAN:
        yr_object_set_integer(external->value.i, obj, NULL);
        break;

      case EXTERNAL_VARIABLE_TYPE_FLOAT:
        yr_object_set_float(external->value.f, obj, NULL);
        break;

      case EXTERNAL_VARIABLE_TYPE_STRING:
      case EXTERNAL_VARIABLE_TYPE_MALLOC_STRING:
        yr_object_set_string(
            external->value.s, strlen(external->value.s), obj, NULL);
        break;
    }

    *object = obj;
  }

  return result;
}


int main(int argc, char** argv)
{
  yr_initialize();

  test_boolean_operators();
  test_comparison_operators();
  test_arithmetic_operators();
  test_bitwise_operators();
  test_matches_operator();
  test_syntax();
  test_anonymous_strings();
  test_strings();
  test_wildcard_strings();
  test_hex_strings();
  test_count();
  test_at();
  test_in();
  test_offset();
  test_length();
  test_of();
  test_for();
  test_re();
  test_filesize();
  // test_compile_file();
  // test_compile_files();
  // test_include_files();
  // test_externals();
  // test_callback();
  // test_compare();
  test_comments();
  test_modules();
  test_integer_functions();
  // test_string_io();
  test_entrypoint();
  test_global_rules();

  #if defined(HASH_MODULE)
  test_hash_module();
  #endif

  test_file_descriptor();

  yr_finalize();

  return 0;
}


int yr_object_dict_set_item(
    YR_OBJECT* object,
    YR_OBJECT* item,
    const char* key)
{
  YR_OBJECT_DICTIONARY* dict;

  int i;
  int count;

  assert(object->type == OBJECT_TYPE_DICTIONARY);

  dict = ((YR_OBJECT_DICTIONARY*) object);

  if (dict->items == NULL)
  {
    count = 64;

    dict->items = (YR_DICTIONARY_ITEMS*) yr_malloc(
        sizeof(YR_DICTIONARY_ITEMS) + count * sizeof(dict->items->objects[0]));

    if (dict->items == NULL)
      return ERROR_INSUFFICIENT_MEMORY;

    memset(dict->items->objects, 0, count * sizeof(dict->items->objects[0]));

    dict->items->free = count;
    dict->items->used = 0;
  }
  else if (dict->items->free == 0)
  {
    count = dict->items->used * 2;
    dict->items = (YR_DICTIONARY_ITEMS*) yr_realloc(
        dict->items,
        sizeof(YR_DICTIONARY_ITEMS) + count * sizeof(dict->items->objects[0]));

    if (dict->items == NULL)
      return ERROR_INSUFFICIENT_MEMORY;

    for (i = dict->items->used; i < count; i++)
    {
      dict->items->objects[i].key = NULL;
      dict->items->objects[i].obj = NULL;
    }

    dict->items->free = dict->items->used;
  }

  item->parent = object;

  dict->items->objects[dict->items->used].key = yr_strdup(key);
  dict->items->objects[dict->items->used].obj = item;

  dict->items->used++;
  dict->items->free--;

  return ERROR_SUCCESS;
}


static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
{
	return usb_serial_generic_open(tty, port);
}


static int omninet_attach(struct usb_serial *serial)
{
	/* The second bulk-out endpoint is used for writing. */
	if (serial->num_bulk_out < 2) {
		dev_err(&serial->interface->dev, "missing endpoints\n");
		return -ENODEV;
	}

	return 0;
}


static int do_boot_mode(struct edgeport_serial *serial,
		const struct firmware *fw)
{
	struct device *dev = &serial->serial->interface->dev;
	int status = 0;
	struct edge_ti_manuf_descriptor *ti_manuf_desc;
	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;

	dev_dbg(dev, "%s - RUNNING IN BOOT MODE\n", __func__);

	/* Configure the TI device so we can use the BULK pipes for download */
	status = config_boot_dev(serial->serial->dev);
	if (status)
		return status;

	if (le16_to_cpu(serial->serial->dev->descriptor.idVendor)
							!= USB_VENDOR_ID_ION) {
		dev_dbg(dev, "%s - VID = 0x%x\n", __func__,
			le16_to_cpu(serial->serial->dev->descriptor.idVendor));
		serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
		goto stayinbootmode;
	}

	/*
	 * We have an ION device (I2c Must be programmed)
	 * Determine I2C image type
	 */
	if (i2c_type_bootmode(serial))
		goto stayinbootmode;

	/* Check for ION Vendor ID and that the I2C is valid */
	if (!check_i2c_image(serial)) {
		struct ti_i2c_image_header *header;
		int i;
		__u8 cs = 0;
		__u8 *buffer;
		int buffer_size;

		/*
		 * Validate Hardware version number
		 * Read Manufacturing Descriptor from TI Based Edgeport
		 */
		ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
		if (!ti_manuf_desc)
			return -ENOMEM;

		status = get_manuf_info(serial, (__u8 *)ti_manuf_desc);
		if (status) {
			kfree(ti_manuf_desc);
			goto stayinbootmode;
		}

		/* Check for version 2 */
		if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
			dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
				__func__, ti_cpu_rev(ti_manuf_desc));
			kfree(ti_manuf_desc);
			goto stayinbootmode;
		}

		kfree(ti_manuf_desc);

		/*
		 * In order to update the I2C firmware we must change the type
		 * 2 record to type 0xF2. This will force the UMP to come up
		 * in Boot Mode.  Then while in boot mode, the driver will
		 * download the latest firmware (padded to 15.5k) into the
		 * UMP ram. Finally when the device comes back up in download
		 * mode the driver will cause the new firmware to be copied
		 * from the UMP Ram to I2C and the firmware will update the
		 * record type from 0xf2 to 0x02.
		 *
		 * Do we really have to copy the whole firmware image,
		 * or could we do this in place!
		 */

		/* Allocate a 15.5k buffer + 3 byte header */
		buffer_size = (((1024 * 16) - 512) +
					sizeof(struct ti_i2c_image_header));
		buffer = kmalloc(buffer_size, GFP_KERNEL);
		if (!buffer)
			return -ENOMEM;

		/* Initialize the buffer to 0xff (pad the buffer) */
		memset(buffer, 0xff, buffer_size);
		memcpy(buffer, &fw->data[4], fw->size - 4);

		for (i = sizeof(struct ti_i2c_image_header);
				i < buffer_size; i++) {
			cs = (__u8)(cs + buffer[i]);
		}

		header = (struct ti_i2c_image_header *)buffer;

		/* update length and checksum after padding */
		header->Length 	 = cpu_to_le16((__u16)(buffer_size -
					sizeof(struct ti_i2c_image_header)));
		header->CheckSum = cs;

		/* Download the operational code  */
		dev_dbg(dev, "%s - Downloading operational code image version %d.%d (TI UMP)\n",
				__func__,
				fw_hdr->major_version, fw_hdr->minor_version);
		status = download_code(serial, buffer, buffer_size);

		kfree(buffer);

		if (status) {
			dev_dbg(dev, "%s - Error downloading operational code image\n", __func__);
			return status;
		}

		/* Device will reboot */
		serial->product_info.TiMode = TI_MODE_TRANSITIONING;

		dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);

		return 1;
	}

stayinbootmode:
	/* Eprom is invalid or blank stay in boot mode */
	dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
	serial->product_info.TiMode = TI_MODE_BOOT;

	return 1;
}


static void edge_throttle(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
	int status;

	if (edge_port == NULL)
		return;

	/* if we are implementing XON/XOFF, send the stop character */
	if (I_IXOFF(tty)) {
		unsigned char stop_char = STOP_CHAR(tty);
		status = edge_write(tty, port, &stop_char, 1);
		if (status <= 0) {
			dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status);
		}
	}

	/*
	 * if we are implementing RTS/CTS, stop reads
	 * and the Edgeport will clear the RTS line
	 */
	if (C_CRTSCTS(tty))
		stop_read(edge_port);

}


static void stop_read(struct edgeport_port *edge_port)
{
	unsigned long flags;

	spin_lock_irqsave(&edge_port->ep_lock, flags);

	if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING)
		edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPING;
	edge_port->shadow_mcr &= ~MCR_RTS;

	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
}


static int choose_config(struct usb_device *dev)
{
	/*
	 * There may be multiple configurations on this device, in which case
	 * we would need to read and parse all of them to find out which one
	 * we want. However, we just support one config at this point,
	 * configuration # 1, which is Config Descriptor 0.
	 */

	dev_dbg(&dev->dev, "%s - Number of Interfaces = %d\n",
		__func__, dev->config->desc.bNumInterfaces);
	dev_dbg(&dev->dev, "%s - MAX Power            = %d\n",
		__func__, dev->config->desc.bMaxPower * 2);

	if (dev->config->desc.bNumInterfaces != 1) {
		dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__);
		return -ENODEV;
	}

	return 0;
}


static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer)
{
	__u16 i;
	__u8 cs = 0;

	for (i = 0; i < le16_to_cpu(rom_desc->Size); i++)
		cs = (__u8)(cs + buffer[i]);

	if (cs != rom_desc->CheckSum) {
		pr_debug("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
		return -EINVAL;
	}
	return 0;
}


static int edge_startup(struct usb_serial *serial)
{
	struct edgeport_serial *edge_serial;
	int status;
	u16 product_id;

	/* Make sure we have the required endpoints when in download mode. */
	if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
		if (serial->num_bulk_in < serial->num_ports ||
				serial->num_bulk_out < serial->num_ports)
			return -ENODEV;
	}

	/* create our private serial structure */
	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
	if (!edge_serial)
		return -ENOMEM;

	mutex_init(&edge_serial->es_lock);
	edge_serial->serial = serial;
	INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
	usb_set_serial_data(serial, edge_serial);

	status = download_fw(edge_serial);
	if (status < 0) {
		kfree(edge_serial);
		return status;
	}

	if (status > 0)
		return 1;	/* bind but do not register any ports */

	product_id = le16_to_cpu(
			edge_serial->serial->dev->descriptor.idProduct);

	/* Currently only the EP/416 models require heartbeat support */
	if (edge_serial->fw_version > FW_HEARTBEAT_VERSION_CUTOFF) {
		if (product_id == ION_DEVICE_ID_TI_EDGEPORT_416 ||
			product_id == ION_DEVICE_ID_TI_EDGEPORT_416B) {
			edge_serial->use_heartbeat = true;
		}
	}

	edge_heartbeat_schedule(edge_serial);

	return 0;
}


static int edge_ioctl(struct tty_struct *tty,
					unsigned int cmd, unsigned long arg)
{
	struct usb_serial_port *port = tty->driver_data;
	struct edgeport_port *edge_port = usb_get_serial_port_data(port);

	switch (cmd) {
	case TIOCGSERIAL:
		dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
		return get_serial_info(edge_port,
				(struct serial_struct __user *) arg);
	}
	return -ENOIOCTLCMD;
}


static int edge_create_sysfs_attrs(struct usb_serial_port *port)
{
	return device_create_file(&port->dev, &dev_attr_uart_mode);
}


void CLASS lin_interpolate()
{
  int code[16][16][32], size = 16, *ip, sum[4];
  int f, c, x, y, row, col, shift, color;

#ifdef DCRAW_VERBOSE
  if (verbose)
    fprintf(stderr, _("Bilinear interpolation...\n"));
#endif
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 0, 3);
#endif

  if (filters == 9)
    size = 6;
  border_interpolate(1);
  for (row = 0; row < size; row++)
    for (col = 0; col < size; col++)
    {
      ip = code[row][col] + 1;
      f = fcol(row, col);
      memset(sum, 0, sizeof sum);
      for (y = -1; y <= 1; y++)
        for (x = -1; x <= 1; x++)
        {
          shift = (y == 0) + (x == 0);
          color = fcol(row + y, col + x);
          if (color == f)
            continue;
          *ip++ = (width * y + x) * 4 + color;
          *ip++ = shift;
          *ip++ = color;
          sum[color] += 1 << shift;
        }
      code[row][col][0] = (ip - code[row][col]) / 3;
      FORCC
      if (c != f)
      {
        *ip++ = c;
        *ip++ = sum[c] > 0 ? 256 / sum[c] : 0;
      }
    }
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 1, 3);
#endif
  lin_interpolate_loop(code, size);
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 2, 3);
#endif
}


void CLASS phase_one_load_raw()
{
  int a, b, i;
  ushort akey, bkey, t_mask;

  fseek(ifp, ph1.key_off, SEEK_SET);
  akey = get2();
  bkey = get2();
  t_mask = ph1.format == 1 ? 0x5555 : 0x1354;
#ifdef LIBRAW_LIBRARY_BUILD
  if (ph1.black_col || ph1.black_row)
  {
    imgdata.rawdata.ph1_cblack = (short(*)[2])calloc(raw_height * 2, sizeof(ushort));
    merror(imgdata.rawdata.ph1_cblack, "phase_one_load_raw()");
    imgdata.rawdata.ph1_rblack = (short(*)[2])calloc(raw_width * 2, sizeof(ushort));
    merror(imgdata.rawdata.ph1_rblack, "phase_one_load_raw()");
    if (ph1.black_col)
    {
      fseek(ifp, ph1.black_col, SEEK_SET);
      read_shorts((ushort *)imgdata.rawdata.ph1_cblack[0], raw_height * 2);
    }
    if (ph1.black_row)
    {
      fseek(ifp, ph1.black_row, SEEK_SET);
      read_shorts((ushort *)imgdata.rawdata.ph1_rblack[0], raw_width * 2);
    }
  }
#endif
  fseek(ifp, data_offset, SEEK_SET);
  read_shorts(raw_image, raw_width * raw_height);
  if (ph1.format)
    for (i = 0; i < raw_width * raw_height; i += 2)
    {
      a = raw_image[i + 0] ^ akey;
      b = raw_image[i + 1] ^ bkey;
      raw_image[i + 0] = (a & t_mask) | (b & ~t_mask);
      raw_image[i + 1] = (b & t_mask) | (a & ~t_mask);
    }
}


void CLASS ljpeg_end(struct jhead *jh)
{
  int c;
  FORC4 if (jh->free[c]) free(jh->free[c]);
  free(jh->row);
}


void CLASS broadcom_load_raw()
{

  uchar *data, *dp;
  int rev, row, col, c;

  rev = 3 * (order == 0x4949);
  data = (uchar *)malloc(raw_stride * 2);
  merror(data, "broadcom_load_raw()");

  for (row = 0; row < raw_height; row++)
  {
    if (fread(data + raw_stride, 1, raw_stride, ifp) < raw_stride)
      derror();
    FORC(raw_stride) data[c] = data[raw_stride + (c ^ rev)];
    for (dp = data, col = 0; col < raw_width; dp += 5, col += 4)
      FORC4 RAW(row, col + c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
  }
  free(data);
}


char *my_memmem(char *haystack, size_t haystacklen, char *needle, size_t needlelen)
{
  char *c;
  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
    if (!memcmp(c, needle, needlelen))
      return c;
  return 0;
}


static size_t local_strnlen(const char *s, size_t n)
{
  const char *p = (const char *)memchr(s, 0, n);
  return (p ? p - s : n);
}