static bool _save_block_index_record (struct sieve_binary *sbin, struct ostream *stream, unsigned int id) { struct sieve_binary_block *block; struct sieve_binary_block_index header; block = sieve_binary_block_get(sbin, id); if ( block == NULL ) return FALSE; header.id = id; header.size = buffer_get_used_size(block->data); header.ext_id = block->ext_index; header.offset = block->offset; if ( !_save_full(sbin, stream, &header, sizeof(header)) ) { sieve_sys_error(sbin->svinst, "binary save: failed to save block index header %d", id); return FALSE; } return TRUE; }
static bool _save_block (struct sieve_binary *sbin, struct ostream *stream, unsigned int id) { struct sieve_binary_block_header block_header; struct sieve_binary_block *block; const void *data; size_t size; block = sieve_binary_block_get(sbin, id); if ( block == NULL ) return FALSE; data = buffer_get_data(block->data, &size); block_header.id = id; block_header.size = size; if ( !_save_aligned(sbin, stream, &block_header, sizeof(block_header), &block->offset) ) return FALSE; return _save_aligned(sbin, stream, data, size, NULL); }
struct sieve_binary *sieve_generator_run (struct sieve_generator *gentr, struct sieve_binary_block **sblock_r) { bool topmost = ( sblock_r == NULL || *sblock_r == NULL ); struct sieve_binary *sbin; struct sieve_binary_block *sblock, *debug_block; const struct sieve_extension *const *extensions; unsigned int i, ext_count; bool result = TRUE; /* Initialize */ if ( topmost ) { sbin = sieve_binary_create_new(sieve_ast_script(gentr->genenv.ast)); sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_MAIN_PROGRAM); } else { sblock = *sblock_r; sbin = sieve_binary_block_get_binary(sblock); } i_assert(sbin != NULL); sieve_binary_ref(sbin); gentr->genenv.sbin = sbin; gentr->genenv.sblock = sblock; /* Create debug block */ debug_block = sieve_binary_block_create(sbin); gentr->dwriter = sieve_binary_debug_writer_init(debug_block); (void)sieve_binary_emit_unsigned (sblock, sieve_binary_block_get_id(debug_block)); /* Load extensions linked to the AST and emit a list in code */ extensions = sieve_ast_extensions_get(gentr->genenv.ast, &ext_count); (void) sieve_binary_emit_unsigned(sblock, ext_count); for ( i = 0; i < ext_count; i++ ) { const struct sieve_extension *ext = extensions[i]; /* Link to binary */ (void)sieve_binary_extension_link(sbin, ext); /* Emit */ sieve_binary_emit_extension(sblock, ext, 0); /* Load */ if ( ext->def != NULL && ext->def->generator_load != NULL && !ext->def->generator_load(ext, &gentr->genenv) ) result = FALSE; } /* Generate code */ if ( result ) { if ( !sieve_generate_block (&gentr->genenv, sieve_ast_root(gentr->genenv.ast))) result = FALSE; else if ( topmost ) sieve_binary_activate(sbin); } /* Cleanup */ gentr->genenv.sbin = NULL; gentr->genenv.sblock = NULL; sieve_binary_unref(&sbin); if ( !result ) { if ( topmost ) { sieve_binary_unref(&sbin); if ( sblock_r != NULL ) *sblock_r = NULL; } sbin = NULL; } else { if ( sblock_r != NULL ) *sblock_r = sblock; } return sbin; }
static bool _sieve_binary_open(struct sieve_binary *sbin) { bool result = TRUE; off_t offset = 0; const struct sieve_binary_header *header; struct sieve_binary_block *ext_block; unsigned int i, blk_count; int ret; /* Verify header */ T_BEGIN { header = LOAD_HEADER(sbin, &offset, const struct sieve_binary_header); /* Check header presence */ if ( header == NULL ) { sieve_sys_error(sbin->svinst, "binary_open: file %s is not large enough to contain the header.", sbin->path); result = FALSE; /* Check header validity */ } else if ( header->magic != SIEVE_BINARY_MAGIC ) { if ( header->magic != SIEVE_BINARY_MAGIC_OTHER_ENDIAN ) sieve_sys_error(sbin->svinst, "binary_open: binary %s has corrupted header " "(0x%08x) or it is not a Sieve binary", sbin->path, header->magic); else if ( sbin->svinst->debug ) sieve_sys_debug(sbin->svinst, "binary open: binary %s stored with in different endian format " "(automatically fixed when re-compiled)", sbin->path); result = FALSE; /* Check binary version */ } else if ( result && ( header->version_major != SIEVE_BINARY_VERSION_MAJOR || header->version_minor != SIEVE_BINARY_VERSION_MINOR ) ) { /* Binary is of different version. Caller will have to recompile */ if ( sbin->svinst->debug ) { sieve_sys_debug(sbin->svinst, "binary open: binary %s stored with different binary version %d.%d " "(!= %d.%d; automatically fixed when re-compiled)", sbin->path, (int) header->version_major, header->version_minor, SIEVE_BINARY_VERSION_MAJOR, SIEVE_BINARY_VERSION_MINOR); } result = FALSE; /* Check block content */ } else if ( result && header->blocks == 0 ) { sieve_sys_error(sbin->svinst, "binary open: binary %s is corrupt: it contains no blocks", sbin->path); result = FALSE; /* Valid */ } else { blk_count = header->blocks; } } T_END; if ( !result ) return FALSE; /* Load block index */ for ( i = 0; i < blk_count && result; i++ ) { T_BEGIN { if ( !_read_block_index_record(sbin, &offset, i) ) { result = FALSE; } } T_END; } if ( !result ) return FALSE; /* Load extensions used by this binary */ T_BEGIN { ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); if ( ext_block == NULL ) { result = FALSE; } else if ( (ret=_read_extensions(ext_block)) <= 0 ) { if ( ret < 0 ) { sieve_sys_error(sbin->svinst, "binary open: binary %s is corrupt: failed to load extension block", sbin->path); } result = FALSE; } } T_END; return result; }
static bool _sieve_binary_save (struct sieve_binary *sbin, struct ostream *stream) { struct sieve_binary_header header; struct sieve_binary_extension_reg *const *regs; struct sieve_binary_block *ext_block; unsigned int ext_count, blk_count, i; uoff_t block_index; blk_count = sieve_binary_block_count(sbin); /* Signal all extensions to finish generating their blocks */ regs = array_get(&sbin->extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) { const struct sieve_binary_extension *binext = regs[i]->binext; if ( binext != NULL && binext->binary_save != NULL ) binext->binary_save(regs[i]->extension, sbin, regs[i]->context); } /* Create header */ header.magic = SIEVE_BINARY_MAGIC; header.version_major = SIEVE_BINARY_VERSION_MAJOR; header.version_minor = SIEVE_BINARY_VERSION_MINOR; header.blocks = blk_count; if ( !_save_aligned(sbin, stream, &header, sizeof(header), NULL) ) { sieve_sys_error(sbin->svinst, "binary save: failed to save header"); return FALSE; } /* Skip block index for now */ if ( !_save_skip_aligned(sbin, stream, sizeof(struct sieve_binary_block_index) * blk_count, &block_index) ) return FALSE; /* Create block containing all used extensions */ ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); i_assert( ext_block != NULL ); sieve_binary_block_clear(ext_block); ext_count = array_count(&sbin->linked_extensions); sieve_binary_emit_unsigned(ext_block, ext_count); for ( i = 0; i < ext_count; i++ ) { struct sieve_binary_extension_reg * const *ext = array_idx(&sbin->linked_extensions, i); sieve_binary_emit_cstring (ext_block, sieve_extension_name((*ext)->extension)); sieve_binary_emit_unsigned (ext_block, sieve_extension_version((*ext)->extension)); sieve_binary_emit_unsigned(ext_block, (*ext)->block_id); } /* Save all blocks into the binary */ for ( i = 0; i < blk_count; i++ ) { if ( !_save_block(sbin, stream, i) ) return FALSE; } /* Create the block index */ o_stream_seek(stream, block_index); for ( i = 0; i < blk_count; i++ ) { if ( !_save_block_index_record(sbin, stream, i) ) return FALSE; } return TRUE; }