sdbox-file.c revision 2a77044395c864cc791cecd34b03002094f4973b
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina/* Copyright (c) 2007-2015 Dovecot authors, see the included COPYING file */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "lib.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "eacces-error.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "fdatasync-path.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "mkdir-parents.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "istream.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "ostream.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "str.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "fs-api.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "dbox-attachment.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "sdbox-storage.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "sdbox-file.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include <stdio.h>
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include <utime.h>
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void sdbox_file_init_paths(struct sdbox_file *file, const char *fname)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mailbox *box = &file->mbox->box;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *alt_path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_free(file->file.primary_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_free(file->file.alt_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->file.primary_path =
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_strdup_printf("%s/%s", mailbox_get_path(box), fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina &alt_path) > 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->file.alt_path = i_strdup_printf("%s/%s", alt_path, fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct sdbox_file *file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *fname;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file = i_new(struct sdbox_file, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->file.storage = &mbox->storage->storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->mbox = mbox;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina T_BEGIN {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (uid != 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_init_paths(file, fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->uid = uid;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_init_paths(file, dbox_generate_tmp_filename());
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } T_END;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_init(&file->file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return &file->file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct dbox_file *sdbox_file_create(struct sdbox_mailbox *mbox)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_file *file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file = sdbox_file_init(mbox, 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->fd = file->storage->v.
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file_create_fd(file, file->primary_path, FALSE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinavoid sdbox_file_free(struct dbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct sdbox_file *sfile = (struct sdbox_file *)file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (sfile->attachment_pool != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_unref(&sfile->attachment_pool);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_free(file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_get_attachments(struct dbox_file *file, const char **extrefs_r)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *line;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina bool deleted;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina *extrefs_r = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* read the metadata */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = dbox_file_open(file, &deleted);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret > 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (deleted)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if ((ret = dbox_file_seek(file, 0)) > 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = dbox_file_metadata_read(file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret <= 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* corrupted file. we're deleting it anyway. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (line == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* no attachments */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina *extrefs_r = line;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaconst char *
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinasdbox_file_attachment_relpath(struct sdbox_file *file, const char *srcpath)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p = strchr(srcpath, '-');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (p == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(file->mbox->box.storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "sdbox attachment path in invalid format: %s", srcpath);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p = strchr(p+1, '-');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return t_strdup_printf("%s-%s-%u",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p == NULL ? srcpath : t_strdup_until(srcpath, p),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina guid_128_to_string(file->mbox->mailbox_guid),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->uid);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int sdbox_file_rename_attachments(struct sdbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = file->file.storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct fs_file *src_file, *dest_file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *const *pathp, *src, *dest;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret = 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_foreach(&file->attachment_paths, pathp) T_BEGIN {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina src = t_strdup_printf("%s/%s", storage->attachment_dir, *pathp);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest = t_strdup_printf("%s/%s", storage->attachment_dir,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_attachment_relpath(file, *pathp));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina src_file = fs_file_init(storage->attachment_fs, src,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest_file = fs_file_init(storage->attachment_fs, dest,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fs_rename(src_file, dest_file) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_last_error(storage->attachment_fs));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file_deinit(&src_file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file_deinit(&dest_file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } T_END;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p, *old_path, *dir, *new_fname, *new_path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct stat st;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_assert(file->uid == 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_assert(uid != 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina old_path = file->file.cur_path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p = strrchr(old_path, '/');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_assert(p != NULL);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dir = t_strdup_until(old_path, p);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina new_fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina new_path = t_strdup_printf("%s/%s", dir, new_fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (stat(new_path, &st) == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&file->file.storage->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "sdbox: %s already exists, rebuilding index", new_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_set_mailbox_corrupted(&file->mbox->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (rename(old_path, new_path) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&file->file.storage->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "rename(%s, %s) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina old_path, new_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_init_paths(file, new_fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->uid = uid;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (array_is_created(&file->attachment_paths)) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (sdbox_file_rename_attachments(file) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int sdbox_file_unlink_aborted_save_attachments(struct sdbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = file->file.storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct fs *fs = storage->attachment_fs;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct fs_file *fs_file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *const *pathp, *path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret = 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_foreach(&file->attachment_paths, pathp) T_BEGIN {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we don't know if we aborted before renaming this attachment,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina so try deleting both source and dest path. the source paths
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina point to temporary files (not to source messages'
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina attachment paths), so it's safe to delete them. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path = t_strdup_printf("%s/%s", storage->attachment_dir,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina *pathp);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fs_delete(fs_file) < 0 &&
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina errno != ENOENT) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_last_error(fs));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file_deinit(&fs_file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path = t_strdup_printf("%s/%s", storage->attachment_dir,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_attachment_relpath(file, *pathp));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fs_delete(fs_file) < 0 &&
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina errno != ENOENT) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_last_error(fs));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file_deinit(&fs_file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } T_END;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_unlink_aborted_save(struct sdbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret = 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (unlink(file->file.cur_path) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(file->mbox->box.storage,
b0c4eb194cf1414d3440e0cccfb9af9074388c08Pavel Březina "unlink(%s) failed: %m", file->file.cur_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (array_is_created(&file->attachment_paths)) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (sdbox_file_unlink_aborted_save_attachments(file) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct sdbox_file *sfile = (struct sdbox_file *)file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mailbox *box = &sfile->mbox->box;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const struct mailbox_permissions *perm = mailbox_get_permissions(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p, *dir;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mode_t old_mask;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int fd;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina old_mask = umask(0666 & ~perm->file_create_mode);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina umask(old_mask);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fd == -1 && errno == ENOENT && parents &&
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (p = strrchr(path, '/')) != NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dir = t_strdup_until(path, p);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mkdir_parents_chgrp(dir, perm->dir_create_mode,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina perm->file_create_gid,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina perm->file_create_gid_origin) < 0 &&
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina errno != EEXIST) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(box->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "mkdir_parents(%s) failed: %m", dir);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* try again */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina old_mask = umask(0666 & ~perm->file_create_mode);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina umask(old_mask);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fd == -1) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(box->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "open(%s, O_CREAT) failed: %m", path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (perm->file_create_gid == (gid_t)-1) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* no group change */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (errno == EPERM) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(box->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina eperm_error_get_chgrp("fchown", path,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina perm->file_create_gid,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina perm->file_create_gid_origin));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(box->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "fchown(%s, -1, %ld) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path, (long)perm->file_create_gid);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* continue anyway */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return fd;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_move(struct dbox_file *file, bool alt_path)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mail_storage *storage = &file->storage->storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct ostream *output;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *dest_dir, *temp_path, *dest_path, *p;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct stat st;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct utimbuf ut;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina bool deleted;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int out_fd, ret = 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_assert(file->input != NULL);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (dbox_file_is_in_alt(file) == alt_path)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (stat(file->cur_path, &st) < 0 && errno == ENOENT) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* already expunged/moved by another session */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest_path = !alt_path ? file->primary_path : file->alt_path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p = strrchr(dest_path, '/');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_assert(p != NULL);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest_dir = t_strdup_until(dest_path, p);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina temp_path = t_strdup_printf("%s/%s", dest_dir,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_generate_tmp_filename());
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* first copy the file. make sure to catch every possible error
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina since we really don't want to break the file. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina out_fd = file->storage->v.file_create_fd(file, temp_path, TRUE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (out_fd == -1)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina output = o_stream_create_fd_file(out_fd, 0, FALSE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_seek(file->input, 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina while ((ret = o_stream_send_istream(output, file->input)) > 0) ;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (o_stream_nfinish(output) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage, "write(%s) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (file->input->stream_errno != 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina errno = file->input->stream_errno;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_set_syscall_error(file, "ftruncate()");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (ret < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "o_stream_send_istream(%s, %s) "
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "failed with unknown error",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina temp_path, file->cur_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_unref(&output);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER && ret == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fsync(out_fd) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "fsync(%s) failed: %m", temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (close(out_fd) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "close(%s) failed: %m", temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)unlink(temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* preserve the original atime/mtime. this isn't necessary for Dovecot,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina but could be useful for external reasons. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ut.actime = st.st_atime;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ut.modtime = st.st_mtime;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (utime(temp_path, &ut) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "utime(%s) failed: %m", temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* the temp file was successfully written. rename it now to the
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina destination file. the destination shouldn't exist, but if it does
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina its contents should be the same (except for maybe older metadata) */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (rename(temp_path, dest_path) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "rename(%s, %s) failed: %m", temp_path, dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)unlink(temp_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (fdatasync_path(dest_dir) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "fdatasync(%s) failed: %m", dest_dir);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)unlink(dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (unlink(file->cur_path) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_set_syscall_error(file, "unlink()");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (errno == EACCES) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* configuration problem? revert the write */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)unlink(dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* who knows what happened to the file. keep both just to be
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sure both won't get deleted. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* file was successfully moved - reopen it */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_close(file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (dbox_file_open(file, &deleted) <= 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "dbox_file_move(%s): reopening file failed", dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinasdbox_unlink_attachments(struct sdbox_file *sfile,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const ARRAY_TYPE(mail_attachment_extref) *extrefs)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = sfile->file.storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const struct mail_attachment_extref *extref;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret = 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_foreach(extrefs, extref) T_BEGIN {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path = sdbox_file_attachment_relpath(sfile, extref->path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (index_attachment_delete(&storage->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina storage->attachment_fs, path) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } T_END;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_unlink_with_attachments(struct sdbox_file *sfile)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ARRAY_TYPE(mail_attachment_extref) extrefs;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *extrefs_line;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_t pool;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = sdbox_file_get_attachments(&sfile->file, &extrefs_line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* no attachments */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return dbox_file_unlink(&sfile->file);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool = pool_alloconly_create("sdbox attachments unlink", 1024);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p_array_init(&extrefs, pool, 16);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (!index_attachment_parse_extrefs(extrefs_line, pool, &extrefs)) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_warning("%s: Ignoring corrupted extref: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sfile->file.cur_path, extrefs_line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_clear(&extrefs);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* try to delete the file first, so if it fails we don't have
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina missing attachments */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if ((ret = dbox_file_unlink(&sfile->file)) >= 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)sdbox_unlink_attachments(sfile, &extrefs);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_unref(&pool);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina