static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir, const char **fname_r) { struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); unsigned int prefix_len; const char *tmp_fname; string_t *path; mode_t old_mask; int fd; path = t_str_new(256); str_append(path, dir); str_append_c(path, '/'); prefix_len = str_len(path); do { tmp_fname = maildir_filename_generate(); str_truncate(path, prefix_len); str_append(path, tmp_fname); /* the generated filename is unique. the only reason why it might return an existing filename is if the time moved backwards. so we'll use O_EXCL anyway, although it's mostly useless. */ old_mask = umask(0777 & ~perm->file_create_mode); fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); umask(old_mask); } while (fd == -1 && errno == EEXIST); *fname_r = tmp_fname; if (fd == -1) { if (ENOQUOTA(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); } else { mail_storage_set_critical(box->storage, "open(%s) failed: %m", str_c(path)); } } else if (perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", str_c(path), perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", str_c(path)); } } } return fd; }
bool mail_storage_copy_can_use_hardlink(struct mailbox *src, struct mailbox *dest) { const struct mailbox_permissions *src_perm = mailbox_get_permissions(src); const struct mailbox_permissions *dest_perm = mailbox_get_permissions(dest); if (src_perm->file_uid != dest_perm->file_uid) { /* if we don't have read permissions, we can't hard link (basically we'll catch 0600 files here) */ if ((src_perm->file_create_mode & 0022) == 0) return FALSE; } return src_perm->file_create_mode == dest_perm->file_create_mode && src_perm->file_create_gid == dest_perm->file_create_gid && !dest->disable_reflink_copy_to; }
static int maildir_keywords_commit(struct maildir_keywords *mk) { const struct mailbox_permissions *perm; struct dotlock *dotlock; const char *lock_path; mode_t old_mask; int i, fd; mk->synced = FALSE; if (!mk->changed || mk->mbox == NULL) return 0; lock_path = t_strconcat(mk->path, ".lock", NULL); i_unlink_if_exists(lock_path); perm = mailbox_get_permissions(&mk->mbox->box); for (i = 0;; i++) { /* we could just create the temp file directly, but doing it this ways avoids potential problems with overwriting contents in malicious symlinks */ old_mask = umask(0777 & ~perm->file_create_mode); fd = file_dotlock_open(&mk->dotlock_settings, mk->path, DOTLOCK_CREATE_FLAG_NONBLOCK, &dotlock); umask(old_mask); if (fd != -1) break; if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) { mail_storage_set_critical(mk->storage, "file_dotlock_open(%s) failed: %m", mk->path); return -1; } /* the control dir doesn't exist. create it unless the whole mailbox was just deleted. */ if (!maildir_set_deleted(&mk->mbox->box)) return -1; } if (maildir_keywords_write_fd(mk, lock_path, fd) < 0) { file_dotlock_delete(&dotlock); return -1; } if (file_dotlock_replace(&dotlock, 0) < 0) { mail_storage_set_critical(mk->storage, "file_dotlock_replace(%s) failed: %m", mk->path); return -1; } mk->changed = FALSE; return 0; }
static int maildir_keywords_write_fd(struct maildir_keywords *mk, const char *path, int fd) { struct maildir_mailbox *mbox = mk->mbox; struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *const *keywords; unsigned int i, count; string_t *str; struct stat st; str = t_str_new(256); keywords = array_get(&mk->list, &count); for (i = 0; i < count; i++) { if (keywords[i] != NULL) str_printfa(str, "%u %s\n", i, keywords[i]); } if (write_full(fd, str_data(str), str_len(str)) < 0) { mail_storage_set_critical(mk->storage, "write_full(%s) failed: %m", path); return -1; } if (fstat(fd, &st) < 0) { mail_storage_set_critical(mk->storage, "fstat(%s) failed: %m", path); return -1; } if (st.st_gid != perm->file_create_gid && perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(mk->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(mk->storage, "fchown(%s) failed: %m", path); } } } /* mtime must grow every time */ if (st.st_mtime <= mk->synced_mtime) { struct utimbuf ut; mk->synced_mtime = ioloop_time <= mk->synced_mtime ? mk->synced_mtime + 1 : ioloop_time; ut.actime = ioloop_time; ut.modtime = mk->synced_mtime; if (utime(path, &ut) < 0) { mail_storage_set_critical(mk->storage, "utime(%s) failed: %m", path); return -1; } } if (fsync(fd) < 0) { mail_storage_set_critical(mk->storage, "fsync(%s) failed: %m", path); return -1; } return 0; }
int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); enum mail_index_open_flags index_flags; int ret; i_assert(!box->opened); index_flags = ibox->index_flags; if (move_to_memory) ibox->index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE; if (index_storage_mailbox_alloc_index(box) < 0) return -1; /* make sure mail_index_set_permissions() has been called */ (void)mailbox_get_permissions(box); ret = mail_index_open(box->index, index_flags); if (ret <= 0 || move_to_memory) { if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) { i_assert(ret <= 0); mailbox_set_index_error(box); return -1; } if (mail_index_move_to_memory(box->index) < 0) { /* try opening once more. it should be created directly into memory now. */ if (mail_index_open_or_create(box->index, index_flags) < 0) i_panic("in-memory index creation failed"); } } if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) { if (mail_index_is_in_memory(box->index)) { mail_storage_set_critical(box->storage, "Couldn't create index file"); mail_index_close(box->index); return -1; } } if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) { if (mail_index_is_deleted(box->index)) { mailbox_set_deleted(box); mail_index_close(box->index); return -1; } } box->cache = mail_index_get_cache(box->index); index_cache_register_defaults(box); box->view = mail_index_view_open(box->index); ibox->keyword_names = mail_index_get_keywords(box->index); ibox->vsize_hdr_ext_id = mail_index_ext_register(box->index, "hdr-vsize", sizeof(struct index_vsize_header), 0, sizeof(uint64_t)); box->opened = TRUE; if ((box->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0) mail_index_modseq_enable(box->index); index_thread_mailbox_opened(box); hook_mailbox_opened(box); return 0; }