static int mbox_write_content_length(struct mbox_save_context *ctx) { uoff_t end_offset; const char *str; size_t len; i_assert(ctx->eoh_offset != (uoff_t)-1); if (ctx->mbox->mbox_writeonly) { /* we can't seek, don't set Content-Length */ return 0; } end_offset = ctx->output->offset; /* write Content-Length headers */ str = t_strdup_printf("\nContent-Length: %s", dec2str(end_offset - ctx->eoh_offset)); len = strlen(str); /* flush manually here so that we don't confuse seek() errors with buffer flushing errors */ if (o_stream_flush(ctx->output) < 0) { write_error(ctx); return -1; } if (o_stream_seek(ctx->output, ctx->extra_hdr_offset + ctx->space_end_idx - len) < 0) { mbox_set_syscall_error(ctx->mbox, "lseek()"); return -1; } if (o_stream_send(ctx->output, str, len) < 0 || o_stream_flush(ctx->output) < 0) { write_error(ctx); return -1; } if (o_stream_seek(ctx->output, end_offset) < 0) { mbox_set_syscall_error(ctx->mbox, "lseek()"); return -1; } return 0; }
static inline bool _save_skip (struct sieve_binary *sbin, struct ostream *stream, size_t size) { if ( (o_stream_seek(stream, stream->offset + size)) <= 0 ) { sieve_sys_error(sbin->svinst, "binary save: failed to skip output stream " "to position %"PRIuUOFF_T": %s", stream->offset + size, strerror(stream->stream_errno)); return FALSE; } return TRUE; }
int mbox_move(struct mbox_sync_context *sync_ctx, uoff_t dest, uoff_t source, uoff_t size) { struct istream *input; struct ostream *output; off_t ret; i_assert(size < OFF_T_MAX); if (size == 0 || source == dest) return 0; i_stream_sync(sync_ctx->input); output = o_stream_create_fd_file(sync_ctx->write_fd, (uoff_t)-1, FALSE); i_stream_seek(sync_ctx->file_input, source); if (o_stream_seek(output, dest) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "o_stream_seek()"); o_stream_unref(&output); return -1; } input = i_stream_create_limit(sync_ctx->file_input, size); ret = o_stream_send_istream(output, input); i_stream_unref(&input); if (ret == (off_t)size) ret = 0; else if (ret >= 0) { mbox_sync_set_critical(sync_ctx, "mbox_move(%"PRIuUOFF_T", %"PRIuUOFF_T", %"PRIuUOFF_T ") moved only %"PRIuUOFF_T" bytes", dest, source, size, (uoff_t)ret); ret = -1; } else if (ret < 0) { errno = output->stream_errno; mbox_set_syscall_error(sync_ctx->mbox, "o_stream_send_istream()"); } mbox_sync_file_updated(sync_ctx, FALSE); o_stream_destroy(&output); return (int)ret; }
static inline bool _save_skip_aligned (struct sieve_binary *sbin, struct ostream *stream, size_t size, uoff_t *offset) { uoff_t aligned_offset = SIEVE_BINARY_ALIGN(stream->offset); if ( (o_stream_seek(stream, aligned_offset + size)) <= 0 ) { sieve_sys_error(sbin->svinst, "binary save: failed to skip output stream " "to position %"PRIuUOFF_T": %s", aligned_offset + size, strerror(stream->stream_errno)); return FALSE; } if ( offset != NULL ) *offset = aligned_offset; return TRUE; }
static void out_clear(void) { o_stream_seek(server->output, 0); str_truncate(out, 0); }
static void test_ostream_file_send_istream_file(void) { struct istream *input, *input2; struct ostream *output; char buf[10]; int fd; test_begin("ostream file send istream file"); /* temp file istream */ fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.istream) failed: %m"); test_assert(write(fd, "1234567890", 10) == 10); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* temp file ostream */ fd = open(".temp.ostream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.ostream) failed: %m"); output = o_stream_create_fd(fd, 0); /* test that writing works between two files */ i_stream_seek(input, 3); input2 = i_stream_create_limit(input, 4); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 4); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4567", 4) == 0); i_stream_unref(&input2); /* test that writing works within the same file */ i_stream_destroy(&input); input = i_stream_create_fd(fd, 1024); /* forwards: 4567 -> 4677 */ o_stream_seek(output, 1); i_stream_seek(input, 2); input2 = i_stream_create_limit(input, 2); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 3); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4677", 4) == 0); i_stream_destroy(&input2); i_stream_destroy(&input); /* backwards: 1234 -> 11234 */ memcpy(buf, "1234", 4); test_assert(pwrite(fd, buf, 4, 0) == 4); input = i_stream_create_fd(fd, 1024); o_stream_seek(output, 1); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 5); test_assert(pread(fd, buf, sizeof(buf), 0) == 5 && memcmp(buf, "11234", 5) == 0); i_stream_destroy(&input); o_stream_destroy(&output); i_close_fd(&fd); i_unlink(".temp.istream"); i_unlink(".temp.ostream"); test_end(); }
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; }