summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/dbox-common/dbox-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/dbox-common/dbox-file.c')
-rw-r--r--src/lib-storage/index/dbox-common/dbox-file.c796
1 files changed, 796 insertions, 0 deletions
diff --git a/src/lib-storage/index/dbox-common/dbox-file.c b/src/lib-storage/index/dbox-common/dbox-file.c
new file mode 100644
index 0000000..16810b0
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-file.c
@@ -0,0 +1,796 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "hex-dec.h"
+#include "hex-binary.h"
+#include "hostpid.h"
+#include "istream.h"
+#include "ostream.h"
+#include "file-lock.h"
+#include "file-dotlock.h"
+#include "mkdir-parents.h"
+#include "eacces-error.h"
+#include "str.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#define DBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE
+
+#ifndef DBOX_FILE_LOCK_METHOD_FLOCK
+static const struct dotlock_settings dotlock_set = {
+ .stale_timeout = 60*10,
+ .use_excl_lock = TRUE
+};
+#endif
+
+const char *dbox_generate_tmp_filename(void)
+{
+ static unsigned int create_count = 0;
+
+ return t_strdup_printf(DBOX_TEMP_FILE_PREFIX"%"PRIdTIME_T".P%sQ%uM%u.%s",
+ ioloop_timeval.tv_sec, my_pid,
+ create_count++,
+ (unsigned int)ioloop_timeval.tv_usec,
+ my_hostname);
+}
+
+void dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
+{
+ mail_storage_set_critical(&file->storage->storage,
+ "%s failed for file %s: %m",
+ function, file->cur_path);
+}
+
+void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
+{
+ va_list args;
+
+ va_start(args, reason);
+ mail_storage_set_critical(&file->storage->storage,
+ "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
+ file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
+ t_strdup_vprintf(reason, args));
+ va_end(args);
+
+ file->storage->v.set_file_corrupted(file);
+}
+
+void dbox_file_init(struct dbox_file *file)
+{
+ file->refcount = 1;
+ file->fd = -1;
+ file->cur_offset = UOFF_T_MAX;
+ file->cur_path = file->primary_path;
+}
+
+void dbox_file_free(struct dbox_file *file)
+{
+ i_assert(file->refcount == 0);
+
+ pool_unref(&file->metadata_pool);
+ dbox_file_close(file);
+ i_free(file->primary_path);
+ i_free(file->alt_path);
+ i_free(file);
+}
+
+void dbox_file_unref(struct dbox_file **_file)
+{
+ struct dbox_file *file = *_file;
+
+ *_file = NULL;
+
+ i_assert(file->refcount > 0);
+ if (--file->refcount == 0)
+ file->storage->v.file_unrefed(file);
+}
+
+static int dbox_file_parse_header(struct dbox_file *file, const char *line)
+{
+ const char *const *tmp, *value;
+ enum dbox_header_key key;
+
+ file->file_version = *line - '0';
+ if (!i_isdigit(line[0]) || line[1] != ' ' ||
+ (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
+ dbox_file_set_corrupted(file, "Invalid dbox version");
+ return -1;
+ }
+ line += 2;
+
+ file->msg_header_size = 0;
+
+ for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
+ uintmax_t time;
+ key = **tmp;
+ value = *tmp + 1;
+
+ switch (key) {
+ case DBOX_HEADER_OLDV1_APPEND_OFFSET:
+ break;
+ case DBOX_HEADER_MSG_HEADER_SIZE:
+ if (str_to_uint_hex(value, &file->msg_header_size) < 0) {
+ dbox_file_set_corrupted(file, "Invalid message header size");
+ return -1;
+ }
+ break;
+ case DBOX_HEADER_CREATE_STAMP:
+ if (str_to_uintmax_hex(value, &time) < 0) {
+ dbox_file_set_corrupted(file, "Invalid create time stamp");
+ return -1;
+ }
+ file->create_time = (time_t)time;
+ break;
+ }
+ }
+
+ if (file->msg_header_size == 0) {
+ dbox_file_set_corrupted(file, "Missing message header size");
+ return -1;
+ }
+ return 0;
+}
+
+static int dbox_file_read_header(struct dbox_file *file)
+{
+ const char *line;
+ unsigned int hdr_size;
+ int ret;
+
+ i_stream_seek(file->input, 0);
+ line = i_stream_read_next_line(file->input);
+ if (line == NULL) {
+ if (file->input->stream_errno == 0) {
+ dbox_file_set_corrupted(file,
+ "EOF while reading file header");
+ return 0;
+ }
+
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ hdr_size = file->input->v_offset;
+ T_BEGIN {
+ ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
+ } T_END;
+ if (ret > 0)
+ file->file_header_size = hdr_size;
+ return ret;
+}
+
+static int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
+{
+ const char *path;
+ int flags = O_RDWR;
+ bool alt = FALSE;
+
+ /* try the primary path first */
+ path = file->primary_path;
+ while ((file->fd = open(path, flags)) == -1) {
+ if (errno == EACCES && flags == O_RDWR) {
+ flags = O_RDONLY;
+ continue;
+ }
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "open(%s) failed: %m", path);
+ return -1;
+ }
+
+ if (file->alt_path == NULL || alt || !try_altpath) {
+ /* not found */
+ return 0;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ file->cur_path = path;
+ return 1;
+}
+
+static int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
+ bool *notfound_r)
+{
+ int ret, fd;
+
+ *notfound_r = FALSE;
+ if (file->input != NULL)
+ return 1;
+
+ if (file->fd == -1) {
+ T_BEGIN {
+ ret = dbox_file_open_fd(file, try_altpath);
+ } T_END;
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ *notfound_r = TRUE;
+ return 1;
+ }
+ }
+
+ /* we're manually checking at dbox_file_close() if we need to close the
+ fd or not. */
+ fd = file->fd;
+ file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE);
+ i_stream_set_name(file->input, file->cur_path);
+ i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
+ return dbox_file_read_header(file);
+}
+
+int dbox_file_open(struct dbox_file *file, bool *deleted_r)
+{
+ return dbox_file_open_full(file, TRUE, deleted_r);
+}
+
+int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
+{
+ return dbox_file_open_full(file, FALSE, notfound_r);
+}
+
+int dbox_file_stat(struct dbox_file *file, struct stat *st_r)
+{
+ const char *path;
+ bool alt = FALSE;
+
+ if (dbox_file_is_open(file)) {
+ if (fstat(file->fd, st_r) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "fstat(%s) failed: %m", file->cur_path);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* try the primary path first */
+ path = file->primary_path;
+ while (stat(path, st_r) < 0) {
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "stat(%s) failed: %m", path);
+ return -1;
+ }
+
+ if (file->alt_path == NULL || alt) {
+ /* not found */
+ return -1;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ file->cur_path = path;
+ return 0;
+}
+
+int dbox_file_header_write(struct dbox_file *file, struct ostream *output)
+{
+ string_t *hdr;
+
+ hdr = t_str_new(128);
+ str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
+ DBOX_HEADER_MSG_HEADER_SIZE,
+ (unsigned int)sizeof(struct dbox_message_header),
+ DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
+
+ file->file_version = DBOX_VERSION;
+ file->file_header_size = str_len(hdr);
+ file->msg_header_size = sizeof(struct dbox_message_header);
+ return o_stream_send(output, str_data(hdr), str_len(hdr));
+}
+
+void dbox_file_close(struct dbox_file *file)
+{
+ dbox_file_unlock(file);
+ if (file->input != NULL) {
+ /* stream autocloses the fd when it gets destroyed. note that
+ the stream may outlive the struct dbox_file. */
+ i_stream_unref(&file->input);
+ file->fd = -1;
+ } else if (file->fd != -1) {
+ if (close(file->fd) < 0)
+ dbox_file_set_syscall_error(file, "close()");
+ file->fd = -1;
+ }
+ file->cur_offset = UOFF_T_MAX;
+}
+
+int dbox_file_try_lock(struct dbox_file *file)
+{
+ const char *error;
+ int ret;
+
+ i_assert(file->fd != -1);
+
+#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
+ struct file_lock_settings lock_set = {
+ .lock_method = FILE_LOCK_METHOD_FLOCK,
+ };
+ ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
+ &lock_set, &file->lock, &error);
+ if (ret < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "file_try_lock(%s) failed: %s", file->cur_path, error);
+ }
+#else
+ ret = file_dotlock_create(&dotlock_set, file->cur_path,
+ DOTLOCK_CREATE_FLAG_NONBLOCK, &file->lock);
+ if (ret < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "file_dotlock_create(%s) failed: %m", file->cur_path);
+ }
+#endif
+ return ret;
+}
+
+void dbox_file_unlock(struct dbox_file *file)
+{
+ i_assert(!file->appending || file->lock == NULL);
+
+ if (file->lock != NULL) {
+#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
+ file_unlock(&file->lock);
+#else
+ file_dotlock_delete(&file->lock);
+#endif
+ }
+ if (file->input != NULL)
+ i_stream_sync(file->input);
+}
+
+int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
+{
+ struct dbox_message_header hdr;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_bytes(file->input, &data, &size,
+ file->msg_header_size);
+ if (ret <= 0) {
+ if (file->input->stream_errno == 0) {
+ /* EOF, broken offset or file truncated */
+ dbox_file_set_corrupted(file, "EOF reading msg header "
+ "(got %zu/%u bytes)",
+ size, file->msg_header_size);
+ return 0;
+ }
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
+ if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
+ /* probably broken offset */
+ dbox_file_set_corrupted(file, "msg header has bad magic value");
+ return 0;
+ }
+
+ if (data[file->msg_header_size-1] != '\n') {
+ dbox_file_set_corrupted(file, "msg header doesn't end with LF");
+ return 0;
+ }
+
+ *physical_size_r = hex2dec(hdr.message_size_hex,
+ sizeof(hdr.message_size_hex));
+ return 1;
+}
+
+int dbox_file_seek(struct dbox_file *file, uoff_t offset)
+{
+ uoff_t size;
+ int ret;
+
+ i_assert(file->input != NULL);
+
+ if (offset == 0)
+ offset = file->file_header_size;
+
+ if (offset != file->cur_offset) {
+ i_stream_seek(file->input, offset);
+ ret = dbox_file_read_mail_header(file, &size);
+ if (ret <= 0)
+ return ret;
+ file->cur_offset = offset;
+ file->cur_physical_size = size;
+ }
+ i_stream_seek(file->input, offset + file->msg_header_size);
+ return 1;
+}
+
+static int
+dbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
+{
+ const char *line;
+ size_t buf_size;
+ int ret;
+
+ i_stream_seek(file->input, *offset);
+ if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
+ return ret;
+
+ /* skip over the actual metadata */
+ buf_size = i_stream_get_max_buffer_size(file->input);
+ i_stream_set_max_buffer_size(file->input, SIZE_MAX);
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ break;
+ }
+ }
+ i_stream_set_max_buffer_size(file->input, buf_size);
+ *offset = file->input->v_offset;
+ return 1;
+}
+
+void dbox_file_seek_rewind(struct dbox_file *file)
+{
+ file->cur_offset = UOFF_T_MAX;
+}
+
+int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
+{
+ uoff_t offset;
+ int ret;
+
+ i_assert(file->input != NULL);
+
+ if (file->cur_offset == UOFF_T_MAX) {
+ /* first mail. we may not have read the file at all yet,
+ so set the offset afterwards. */
+ offset = 0;
+ } else {
+ offset = file->cur_offset + file->msg_header_size +
+ file->cur_physical_size;
+ if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
+ *offset_r = file->cur_offset;
+ return ret;
+ }
+ if (i_stream_read_eof(file->input)) {
+ *last_r = TRUE;
+ return 0;
+ }
+ }
+ *offset_r = offset;
+
+ *last_r = FALSE;
+
+ ret = dbox_file_seek(file, offset);
+ if (*offset_r == 0)
+ *offset_r = file->file_header_size;
+ return ret;
+}
+
+struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
+{
+ struct dbox_file_append_context *ctx;
+
+ i_assert(!file->appending);
+
+ file->appending = TRUE;
+
+ ctx = i_new(struct dbox_file_append_context, 1);
+ ctx->file = file;
+ if (file->fd != -1) {
+ ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
+ o_stream_set_name(ctx->output, file->cur_path);
+ o_stream_set_finish_via_child(ctx->output, FALSE);
+ o_stream_cork(ctx->output);
+ }
+ return ctx;
+}
+
+int dbox_file_append_commit(struct dbox_file_append_context **_ctx)
+{
+ struct dbox_file_append_context *ctx = *_ctx;
+ int ret;
+
+ i_assert(ctx->file->appending);
+
+ *_ctx = NULL;
+
+ ret = dbox_file_append_flush(ctx);
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ o_stream_close(ctx->output);
+ if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "ftruncate()");
+ return -1;
+ }
+ }
+ o_stream_unref(&ctx->output);
+ ctx->file->appending = FALSE;
+ i_free(ctx);
+ return ret;
+}
+
+void dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
+{
+ struct dbox_file_append_context *ctx = *_ctx;
+ struct dbox_file *file = ctx->file;
+ bool close_file = FALSE;
+
+ i_assert(ctx->file->appending);
+
+ *_ctx = NULL;
+ if (ctx->first_append_offset == 0) {
+ /* nothing changed */
+ } else if (ctx->first_append_offset == file->file_header_size) {
+ /* rolling back everything */
+ if (unlink(file->cur_path) < 0)
+ dbox_file_set_syscall_error(file, "unlink()");
+ close_file = TRUE;
+ } else {
+ /* truncating only some mails */
+ o_stream_close(ctx->output);
+ if (ftruncate(file->fd, ctx->first_append_offset) < 0)
+ dbox_file_set_syscall_error(file, "ftruncate()");
+ }
+ if (ctx->output != NULL) {
+ o_stream_abort(ctx->output);
+ o_stream_unref(&ctx->output);
+ }
+ i_free(ctx);
+
+ if (close_file)
+ dbox_file_close(file);
+ file->appending = FALSE;
+}
+
+int dbox_file_append_flush(struct dbox_file_append_context *ctx)
+{
+ struct mail_storage *storage = &ctx->file->storage->storage;
+
+ if (ctx->last_flush_offset == ctx->output->offset &&
+ ctx->last_checkpoint_offset == ctx->output->offset)
+ return 0;
+
+ if (o_stream_flush(ctx->output) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "write()");
+ return -1;
+ }
+
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "ftruncate()");
+ return -1;
+ }
+ if (o_stream_seek(ctx->output, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "lseek()");
+ return -1;
+ }
+ }
+
+ if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
+ if (fdatasync(ctx->file->fd) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "fdatasync()");
+ return -1;
+ }
+ }
+ ctx->last_flush_offset = ctx->output->offset;
+ return 0;
+}
+
+void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx)
+{
+ ctx->last_checkpoint_offset = ctx->output->offset;
+}
+
+int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
+ struct ostream **output_r)
+{
+ struct dbox_file *file = ctx->file;
+ struct stat st;
+
+ if (ctx->output == NULL) {
+ /* file creation had failed */
+ return -1;
+ }
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ /* a message was aborted. don't try appending to this
+ file anymore. */
+ return -1;
+ }
+
+ if (file->file_version == 0) {
+ /* newly created file, write the file header */
+ if (dbox_file_header_write(file, ctx->output) < 0) {
+ dbox_file_set_syscall_error(file, "write()");
+ return -1;
+ }
+ *output_r = ctx->output;
+ return 1;
+ }
+
+ /* file has existing mails */
+ if (file->file_version != DBOX_VERSION ||
+ file->msg_header_size != sizeof(struct dbox_message_header)) {
+ /* created by an incompatible version, can't append */
+ return 0;
+ }
+
+ if (ctx->output->offset == 0) {
+ /* first append to existing file. seek to eof first. */
+ if (fstat(file->fd, &st) < 0) {
+ dbox_file_set_syscall_error(file, "fstat()");
+ return -1;
+ }
+ if (st.st_size < file->msg_header_size) {
+ dbox_file_set_corrupted(file,
+ "dbox file size too small");
+ return 0;
+ }
+ if (o_stream_seek(ctx->output, st.st_size) < 0) {
+ dbox_file_set_syscall_error(file, "lseek()");
+ return -1;
+ }
+ }
+ *output_r = ctx->output;
+ return 1;
+}
+
+int dbox_file_metadata_skip_header(struct dbox_file *file)
+{
+ struct dbox_metadata_header metadata_hdr;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_bytes(file->input, &data, &size,
+ sizeof(metadata_hdr));
+ if (ret <= 0) {
+ if (file->input->stream_errno == 0) {
+ /* EOF, broken offset */
+ dbox_file_set_corrupted(file,
+ "Unexpected EOF while reading metadata header");
+ return 0;
+ }
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ memcpy(&metadata_hdr, data, sizeof(metadata_hdr));
+ if (memcmp(metadata_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(metadata_hdr.magic_post)) != 0) {
+ /* probably broken offset */
+ dbox_file_set_corrupted(file,
+ "metadata header has bad magic value");
+ return 0;
+ }
+ i_stream_skip(file->input, sizeof(metadata_hdr));
+ return 1;
+}
+
+static int
+dbox_file_metadata_read_at(struct dbox_file *file, uoff_t metadata_offset)
+{
+ const char *line;
+ size_t buf_size;
+ int ret;
+
+ if (file->metadata_pool != NULL)
+ p_clear(file->metadata_pool);
+ else {
+ file->metadata_pool =
+ pool_alloconly_create("dbox metadata", 1024);
+ }
+ p_array_init(&file->metadata, file->metadata_pool, 16);
+
+ i_stream_seek(file->input, metadata_offset);
+ if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
+ return ret;
+
+ ret = 0;
+ buf_size = i_stream_get_max_buffer_size(file->input);
+ /* use unlimited line length for metadata */
+ i_stream_set_max_buffer_size(file->input, SIZE_MAX);
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ ret = 1;
+ break;
+ }
+ line = p_strdup(file->metadata_pool, line);
+ array_push_back(&file->metadata, &line);
+ }
+ i_stream_set_max_buffer_size(file->input, buf_size);
+ if (ret == 0)
+ dbox_file_set_corrupted(file, "missing end-of-metadata line");
+ return ret;
+}
+
+int dbox_file_metadata_read(struct dbox_file *file)
+{
+ uoff_t metadata_offset;
+ int ret;
+
+ i_assert(file->cur_offset != UOFF_T_MAX);
+
+ if (file->metadata_read_offset == file->cur_offset)
+ return 1;
+
+ metadata_offset = file->cur_offset + file->msg_header_size +
+ file->cur_physical_size;
+ ret = dbox_file_metadata_read_at(file, metadata_offset);
+ if (ret <= 0)
+ return ret;
+
+ file->metadata_read_offset = file->cur_offset;
+ return 1;
+}
+
+const char *dbox_file_metadata_get(struct dbox_file *file,
+ enum dbox_metadata_key key)
+{
+ const char *const *metadata;
+ unsigned int i, count;
+
+ metadata = array_get(&file->metadata, &count);
+ for (i = 0; i < count; i++) {
+ if (*metadata[i] == (char)key)
+ return metadata[i] + 1;
+ }
+ return NULL;
+}
+
+uoff_t dbox_file_get_plaintext_size(struct dbox_file *file)
+{
+ const char *value;
+ uintmax_t size;
+
+ i_assert(file->metadata_read_offset == file->cur_offset);
+
+ /* see if we have it in metadata */
+ value = dbox_file_metadata_get(file, DBOX_METADATA_PHYSICAL_SIZE);
+ if (value == NULL ||
+ str_to_uintmax_hex(value, &size) < 0 ||
+ size > UOFF_T_MAX) {
+ /* no. that means we can use the size in the header */
+ return file->cur_physical_size;
+ }
+
+ return (uoff_t)size;
+}
+
+void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
+ uoff_t message_size)
+{
+ memset(dbox_msg_hdr, ' ', sizeof(*dbox_msg_hdr));
+ memcpy(dbox_msg_hdr->magic_pre, DBOX_MAGIC_PRE,
+ sizeof(dbox_msg_hdr->magic_pre));
+ dbox_msg_hdr->type = DBOX_MESSAGE_TYPE_NORMAL;
+ dec2hex(dbox_msg_hdr->message_size_hex, message_size,
+ sizeof(dbox_msg_hdr->message_size_hex));
+ dbox_msg_hdr->save_lf = '\n';
+}
+
+int dbox_file_unlink(struct dbox_file *file)
+{
+ const char *path;
+ bool alt = FALSE;
+
+ path = file->primary_path;
+ while (unlink(path) < 0) {
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "unlink(%s) failed: %m", path);
+ return -1;
+ }
+ if (file->alt_path == NULL || alt) {
+ /* not found */
+ return 0;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ return 1;
+}