Mercurial > dovecot > core-2.2
view src/lib-index/mailbox-log.c @ 12262:80a080814041
lib-index: Mailbox log writing was trying to write to a closed log file fd.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 14 Oct 2010 16:17:43 +0100 |
parents | 2b8b2875af26 |
children | 03ac8057710d |
line wrap: on
line source
/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "eacces-error.h" #include "mailbox-log.h" #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #define MAILBOX_LOG_ROTATE_SIZE (1024*4) struct mailbox_log { char *filepath, *filepath2; int fd; time_t open_timestamp; mode_t mode; gid_t gid; char *gid_origin; }; struct mailbox_log_iter { struct mailbox_log *log; int fd; const char *filepath; struct mailbox_log_record buf[128]; unsigned int idx, count; uoff_t offset; bool failed; }; static void mailbox_log_close(struct mailbox_log *log); struct mailbox_log *mailbox_log_alloc(const char *path) { struct mailbox_log *log; log = i_new(struct mailbox_log, 1); log->filepath = i_strdup(path); log->filepath2 = i_strconcat(path, ".2", NULL); log->mode = 0644; log->gid = (gid_t)-1; log->fd = -1; return log; } void mailbox_log_free(struct mailbox_log **_log) { struct mailbox_log *log = *_log; *_log = NULL; mailbox_log_close(log); i_free(log->gid_origin); i_free(log->filepath); i_free(log->filepath2); i_free(log); } static void mailbox_log_close(struct mailbox_log *log) { if (log->fd != -1) { if (close(log->fd) < 0) i_error("close(%s) failed: %m", log->filepath); log->fd = -1; } } void mailbox_log_set_permissions(struct mailbox_log *log, mode_t mode, gid_t gid, const char *gid_origin) { log->mode = mode; log->gid = gid; i_free(log->gid_origin); log->gid_origin = i_strdup(gid_origin); } static int mailbox_log_open(struct mailbox_log *log) { mode_t old_mode; i_assert(log->fd == -1); log->open_timestamp = ioloop_time; log->fd = open(log->filepath, O_RDWR | O_APPEND); if (log->fd != -1) return 0; /* try to create it */ old_mode = umask(0666 ^ log->mode); log->fd = open(log->filepath, O_RDWR | O_APPEND | O_CREAT, 0666); umask(old_mode); if (log->fd == -1) { if (errno != EACCES) i_error("creat(%s) failed: %m", log->filepath); else i_error("%s", eacces_error_get("creat", log->filepath)); return -1; } if (fchown(log->fd, (uid_t)-1, log->gid) < 0) { if (errno != EPERM) i_error("fchown(%s) failed: %m", log->filepath); else { i_error("%s", eperm_error_get_chgrp("fchown", log->filepath, log->gid, log->gid_origin)); } } return 0; } static int mailbox_log_rotate_if_needed(struct mailbox_log *log) { struct stat st; if (fstat(log->fd, &st) < 0) { i_error("fstat(%s) failed: %m", log->filepath); return -1; } if (st.st_size < MAILBOX_LOG_ROTATE_SIZE) return 0; if (rename(log->filepath, log->filepath2) < 0 && errno != ENOENT) { i_error("rename(%s, %s) failed: %m", log->filepath, log->filepath2); return -1; } return 0; } void mailbox_log_record_set_timestamp(struct mailbox_log_record *rec, time_t stamp) { rec->timestamp[0] = (stamp & 0xff000000) >> 24; rec->timestamp[1] = (stamp & 0x00ff0000) >> 16; rec->timestamp[2] = (stamp & 0x0000ff00) >> 8; rec->timestamp[3] = (stamp & 0x000000ff); } time_t mailbox_log_record_get_timestamp(const struct mailbox_log_record *rec) { return ((time_t)rec->timestamp[0] << 24) | ((time_t)rec->timestamp[1] << 16) | ((time_t)rec->timestamp[2] << 8) | (time_t)rec->timestamp[3]; } int mailbox_log_append(struct mailbox_log *log, const struct mailbox_log_record *rec) { struct stat st; ssize_t ret; /* we don't have to be too strict about appending to the latest log file. the records' ordering doesn't matter and iteration goes through both logs anyway. still, if there's a long running session it shouldn't keep writing to a rotated log forever. */ if (log->open_timestamp != ioloop_time) mailbox_log_close(log); if (log->fd == -1) { if (mailbox_log_open(log) < 0) return -1; } /* We don't bother with locking, atomic appends will protect us. If they don't (NFS), the worst that can happen is that a few records get overwritten (because they're all the same size). This whole log isn't supposed to be super-reliable anyway. */ ret = write(log->fd, rec, sizeof(*rec)); if (ret < 0) { i_error("write(%s) failed: %m", log->filepath); return -1; } else if (ret != sizeof(*rec)) { i_error("write(%s) wrote %d/%u bytes", log->filepath, (int)ret, (unsigned int)sizeof(*rec)); if (fstat(log->fd, &st) == 0) { if (ftruncate(log->fd, st.st_size - ret) < 0) { i_error("ftruncate(%s) failed: %m", log->filepath); } } return -1; } (void)mailbox_log_rotate_if_needed(log); return 0; } static bool mailbox_log_iter_open_next(struct mailbox_log_iter *iter) { if (iter->fd != -1) { if (close(iter->fd) < 0) i_error("close(%s) failed: %m", iter->filepath); iter->fd = -1; } if (iter->filepath == NULL) iter->filepath = iter->log->filepath2; else if (iter->filepath == iter->log->filepath2) iter->filepath = iter->log->filepath; else return FALSE; iter->fd = open(iter->filepath, O_RDONLY | O_APPEND); if (iter->fd != -1) return TRUE; else if (errno == ENOENT) { if (iter->filepath == iter->log->filepath2) return mailbox_log_iter_open_next(iter); } else { i_error("open(%s) failed: %m", iter->filepath); iter->failed = TRUE; } return FALSE; } struct mailbox_log_iter *mailbox_log_iter_init(struct mailbox_log *log) { struct mailbox_log_iter *iter; iter = i_new(struct mailbox_log_iter, 1); iter->log = log; iter->fd = -1; (void)mailbox_log_iter_open_next(iter); return iter; } const struct mailbox_log_record * mailbox_log_iter_next(struct mailbox_log_iter *iter) { const struct mailbox_log_record *rec; uoff_t offset; ssize_t ret; if (iter->idx == iter->count) { if (iter->fd == -1) return NULL; ret = pread(iter->fd, iter->buf, sizeof(iter->buf), iter->offset); if (ret < 0) { i_error("pread(%s) failed: %m", iter->filepath); iter->failed = TRUE; return NULL; } if (ret == 0) { if (!mailbox_log_iter_open_next(iter)) return NULL; iter->idx = iter->count = 0; iter->offset = 0; return mailbox_log_iter_next(iter); } iter->idx = 0; iter->count = ret / sizeof(iter->buf[0]); iter->offset += iter->count * sizeof(iter->buf[0]); } rec = &iter->buf[iter->idx++]; if (rec->type < MAILBOX_LOG_RECORD_DELETE_MAILBOX || rec->type > MAILBOX_LOG_RECORD_UNSUBSCRIBE) { offset = iter->offset - (iter->count - iter->idx) * sizeof(iter->buf[0]); i_error("Corrupted mailbox log %s at offset %"PRIuUOFF_T": " "type=%d", iter->filepath, offset, rec->type); if (unlink(iter->filepath) < 0) i_error("unlink(%s) failed: %m", iter->filepath); return NULL; } return rec; } int mailbox_log_iter_deinit(struct mailbox_log_iter **_iter) { struct mailbox_log_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; if (iter->fd != -1) { if (close(iter->fd) < 0) i_error("close(%s) failed: %m", iter->filepath); } i_free(iter); return ret; }