bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainenstatic const struct dotlock_settings dotlock_set = {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen return t_strdup_printf(DBOX_TEMP_FILE_PREFIX"%"PRIdTIME_T".P%sQ%uM%u.%s",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenvoid dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen mail_storage_set_critical(&file->storage->storage,
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen "%s failed for file %s: %m",
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainenvoid dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainenstatic int dbox_file_parse_header(struct dbox_file *file, const char *line)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "Invalid dbox version");
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen if (str_to_uint_hex(value, &file->msg_header_size) < 0) {
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen dbox_file_set_corrupted(file, "Invalid message header size");
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen dbox_file_set_corrupted(file, "Invalid create time stamp");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "Missing message header size");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic int dbox_file_read_header(struct dbox_file *file)
b8c009e1639efa53eb9e70720f28ce137d493e5eTimo Sirainen "EOF while reading file header");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainenstatic int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the primary path first */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen while ((file->fd = open(path, flags)) == -1) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen mail_storage_set_critical(&file->storage->storage,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (file->alt_path == NULL || alt || !try_altpath) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen /* not found */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen /* try the alternative path */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* we're manually checking at dbox_file_close() if we need to close the
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen fd or not. */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_name(file->input, file->cur_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenint dbox_file_open(struct dbox_file *file, bool *deleted_r)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return dbox_file_open_full(file, TRUE, deleted_r);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainenint dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return dbox_file_open_full(file, FALSE, notfound_r);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenint dbox_file_stat(struct dbox_file *file, struct stat *st_r)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the primary path first */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* not found */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the alternative path */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenint dbox_file_header_write(struct dbox_file *file, struct ostream *output)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen (unsigned int)sizeof(struct dbox_message_header),
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->msg_header_size = sizeof(struct dbox_message_header);
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen return o_stream_send(output, str_data(hdr), str_len(hdr));
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* stream autocloses the fd when it gets destroyed. note that
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen the stream may outlive the struct dbox_file. */
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "file_try_lock(%s) failed: %m", file->cur_path);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen ret = file_dotlock_create(&dotlock_set, file->cur_path,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen "file_dotlock_create(%s) failed: %m", file->cur_path);
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen i_assert(!file->appending || file->lock == NULL);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainenint dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
2a8b891366a3fc69524c2bb07f68d42c16223a56Timo Sirainen ret = i_stream_read_bytes(file->input, &data, &size,
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen /* EOF, broken offset or file truncated */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen dbox_file_set_corrupted(file, "EOF reading msg header "
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* probably broken offset */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "msg header has bad magic value");
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen dbox_file_set_corrupted(file, "msg header doesn't end with LF");
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen *physical_size_r = hex2dec(hdr.message_size_hex,
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainenint dbox_file_seek(struct dbox_file *file, uoff_t offset)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen ret = dbox_file_read_mail_header(file, &size);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen i_stream_seek(file->input, offset + file->msg_header_size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainendbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen /* skip over the actual metadata */
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen buf_size = i_stream_get_max_buffer_size(file->input);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen i_stream_set_max_buffer_size(file->input, (size_t)-1);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen while ((line = i_stream_read_next_line(file->input)) != NULL) {
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* end of metadata */
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen i_stream_set_max_buffer_size(file->input, buf_size);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenvoid dbox_file_seek_rewind(struct dbox_file *file)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenint dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* first mail. we may not have read the file at all yet,
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen so set the offset afterwards. */
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen offset = file->cur_offset + file->msg_header_size +
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainenstruct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen ctx = i_new(struct dbox_file_append_context, 1);
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen o_stream_set_name(ctx->output, file->cur_path);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen o_stream_set_finish_via_child(ctx->output, FALSE);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenint dbox_file_append_commit(struct dbox_file_append_context **_ctx)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ctx->last_checkpoint_offset != ctx->output->offset) {
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen dbox_file_set_syscall_error(ctx->file, "ftruncate()");
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenvoid dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen /* nothing changed */
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen } else if (ctx->first_append_offset == file->file_header_size) {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen /* rolling back everything */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen dbox_file_set_syscall_error(file, "unlink()");
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen /* truncating only some mails */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ftruncate(file->fd, ctx->first_append_offset) < 0)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen dbox_file_set_syscall_error(file, "ftruncate()");
if (close_file)
sizeof(metadata_hdr));
if (ret <= 0) {
return ret;
ret = 0;
if (ret == 0)
return ret;
if (ret <= 0)
return ret;
for (i = 0; i < count; i++) {
return NULL;