Mercurial > dovecot > core-2.2
diff src/lib-storage/index/dbox-single/sdbox-file.c @ 11764:8ec35eb6d7b3 HEAD
dbox: dbox_file_move() was used by only single-dbox, so moved it there.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Jul 2010 15:39:03 +0100 |
parents | 1bb98ad1af8b |
children | 28eaaa23f2c6 |
line wrap: on
line diff
--- a/src/lib-storage/index/dbox-single/sdbox-file.c Fri Jul 09 13:07:37 2010 +0100 +++ b/src/lib-storage/index/dbox-single/sdbox-file.c Fri Jul 09 15:39:03 2010 +0100 @@ -2,7 +2,10 @@ #include "lib.h" #include "eacces-error.h" +#include "fdatasync-path.h" #include "mkdir-parents.h" +#include "istream.h" +#include "ostream.h" #include "sdbox-storage.h" #include "sdbox-file.h" @@ -126,3 +129,112 @@ } return fd; } + +int sdbox_file_move(struct dbox_file *file, bool alt_path) +{ + struct mail_storage *storage = &file->storage->storage; + struct ostream *output; + const char *dest_dir, *temp_path, *dest_path, *p; + struct stat st; + bool deleted; + int out_fd, ret = 0; + + i_assert(file->input != NULL); + + if (dbox_file_is_in_alt(file) == alt_path) + return 0; + + if (stat(file->cur_path, &st) < 0 && errno == ENOENT) { + /* already expunged/moved by another session */ + return 0; + } + + dest_path = !alt_path ? file->primary_path : file->alt_path; + p = strrchr(dest_path, '/'); + i_assert(p != NULL); + dest_dir = t_strdup_until(dest_path, p); + temp_path = t_strdup_printf("%s/%s", dest_dir, + dbox_generate_tmp_filename()); + + /* first copy the file. make sure to catch every possible error + since we really don't want to break the file. */ + out_fd = file->storage->v.file_create_fd(file, temp_path, TRUE); + if (out_fd == -1) + return -1; + + output = o_stream_create_fd_file(out_fd, 0, FALSE); + i_stream_seek(file->input, 0); + while ((ret = o_stream_send_istream(output, file->input)) > 0) ; + if (ret == 0) + ret = o_stream_flush(output); + if (output->stream_errno != 0) { + errno = output->stream_errno; + mail_storage_set_critical(storage, "write(%s) failed: %m", + temp_path); + ret = -1; + } else if (file->input->stream_errno != 0) { + errno = file->input->stream_errno; + dbox_file_set_syscall_error(file, "ftruncate()"); + ret = -1; + } else if (ret < 0) { + mail_storage_set_critical(storage, + "o_stream_send_istream(%s, %s) " + "failed with unknown error", + temp_path, file->cur_path); + } + o_stream_unref(&output); + + if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER && ret == 0) { + if (fsync(out_fd) < 0) { + mail_storage_set_critical(storage, + "fsync(%s) failed: %m", temp_path); + ret = -1; + } + } + if (close(out_fd) < 0) { + mail_storage_set_critical(storage, + "close(%s) failed: %m", temp_path); + ret = -1; + } + if (ret < 0) { + (void)unlink(temp_path); + return -1; + } + + /* the temp file was successfully written. rename it now to the + destination file. the destination shouldn't exist, but if it does + its contents should be the same (except for maybe older metadata) */ + if (rename(temp_path, dest_path) < 0) { + mail_storage_set_critical(storage, + "rename(%s, %s) failed: %m", temp_path, dest_path); + (void)unlink(temp_path); + return -1; + } + if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) { + if (fdatasync_path(dest_dir) < 0) { + mail_storage_set_critical(storage, + "fdatasync(%s) failed: %m", dest_dir); + (void)unlink(dest_path); + return -1; + } + } + if (unlink(file->cur_path) < 0) { + dbox_file_set_syscall_error(file, "unlink()"); + if (errno == EACCES) { + /* configuration problem? revert the write */ + (void)unlink(dest_path); + } + /* who knows what happened to the file. keep both just to be + sure both won't get deleted. */ + return -1; + } + + /* file was successfully moved - reopen it */ + dbox_file_close(file); + if (dbox_file_open(file, &deleted) <= 0) { + mail_storage_set_critical(storage, + "dbox_file_move(%s): reopening file failed", dest_path); + return -1; + } + return 0; +}