sdbox-file.c revision 2a77044395c864cc791cecd34b03002094f4973b
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina/* Copyright (c) 2007-2015 Dovecot authors, see the included COPYING file */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void sdbox_file_init_paths(struct sdbox_file *file, const char *fname)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_strdup_printf("%s/%s", mailbox_get_path(box), fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file->file.alt_path = i_strdup_printf("%s/%s", alt_path, fname);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina sdbox_file_init_paths(file, dbox_generate_tmp_filename());
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct dbox_file *sdbox_file_create(struct sdbox_mailbox *mbox)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina file_create_fd(file, file->primary_path, FALSE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct sdbox_file *sfile = (struct sdbox_file *)file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_get_attachments(struct dbox_file *file, const char **extrefs_r)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* read the metadata */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* corrupted file. we're deleting it anyway. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* no attachments */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinasdbox_file_attachment_relpath(struct sdbox_file *file, const char *srcpath)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p;
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 p == NULL ? srcpath : t_strdup_until(srcpath, p),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int sdbox_file_rename_attachments(struct sdbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = file->file.storage;
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 src_file = fs_file_init(storage->attachment_fs, src,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest_file = fs_file_init(storage->attachment_fs, dest,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p, *old_path, *dir, *new_fname, *new_path;
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 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 mail_storage_set_critical(&file->file.storage->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "rename(%s, %s) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (array_is_created(&file->attachment_paths)) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int sdbox_file_unlink_aborted_save_attachments(struct sdbox_file *file)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = file->file.storage;
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 fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path = t_strdup_printf("%s/%s", storage->attachment_dir,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina fs_file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(&storage->storage, "%s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_unlink_aborted_save(struct sdbox_file *file)
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 if (array_is_created(&file->attachment_paths)) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (sdbox_file_unlink_aborted_save_attachments(file) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct sdbox_file *sfile = (struct sdbox_file *)file;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const struct mailbox_permissions *perm = mailbox_get_permissions(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *p, *dir;
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 if (mkdir_parents_chgrp(dir, perm->dir_create_mode,
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 } 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 "fchown(%s, -1, %ld) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* continue anyway */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_move(struct dbox_file *file, bool alt_path)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mail_storage *storage = &file->storage->storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *dest_dir, *temp_path, *dest_path, *p;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (stat(file->cur_path, &st) < 0 && errno == ENOENT) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* already expunged/moved by another session */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dest_path = !alt_path ? file->primary_path : file->alt_path;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina temp_path = t_strdup_printf("%s/%s", dest_dir,
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 output = o_stream_create_fd_file(out_fd, 0, FALSE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina while ((ret = o_stream_send_istream(output, file->input)) > 0) ;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_critical(storage, "write(%s) failed: %m",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_set_syscall_error(file, "ftruncate()");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (ret < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "o_stream_send_istream(%s, %s) "
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "failed with unknown error",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER && ret == 0) {
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 /* 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 "rename(%s, %s) failed: %m", temp_path, dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina dbox_file_set_syscall_error(file, "unlink()");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* configuration problem? revert the write */
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 /* file was successfully moved - reopen it */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "dbox_file_move(%s): reopening file failed", dest_path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinasdbox_unlink_attachments(struct sdbox_file *sfile,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const ARRAY_TYPE(mail_attachment_extref) *extrefs)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct dbox_storage *storage = sfile->file.storage;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina path = sdbox_file_attachment_relpath(sfile, extref->path);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (index_attachment_delete(&storage->storage,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint sdbox_file_unlink_with_attachments(struct sdbox_file *sfile)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = sdbox_file_get_attachments(&sfile->file, &extrefs_line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* no attachments */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool = pool_alloconly_create("sdbox attachments unlink", 1024);
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 /* 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)