maildir-util.c revision 5fb3bff645380804c9db2510940c41db6b8fdb01
178N/A/* Copyright (C) 2004 Timo Sirainen */
178N/A
178N/A#include "lib.h"
178N/A#include "hostpid.h"
178N/A#include "ioloop.h"
178N/A#include "str.h"
178N/A#include "maildir-storage.h"
178N/A#include "maildir-uidlist.h"
178N/A#include "maildir-keywords.h"
178N/A#include "maildir-sync.h"
178N/A
178N/A#include <unistd.h>
178N/A#include <fcntl.h>
178N/A#include <sys/stat.h>
178N/A
178N/Astatic int maildir_file_do_try(struct maildir_mailbox *mbox, uint32_t uid,
178N/A maildir_file_do_func *func, void *context)
178N/A{
178N/A const char *fname, *path;
178N/A enum maildir_uidlist_rec_flag flags;
178N/A int ret;
178N/A
178N/A fname = maildir_uidlist_lookup(mbox->uidlist, uid, &flags);
178N/A if (fname == NULL)
178N/A return -2; /* expunged */
178N/A
178N/A t_push();
178N/A if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
178N/A /* probably in new/ dir */
178N/A path = t_strconcat(mbox->path, "/new/", fname, NULL);
178N/A ret = func(mbox, path, context);
178N/A if (ret != 0) {
178N/A t_pop();
178N/A return ret;
178N/A }
178N/A }
178N/A
178N/A path = t_strconcat(mbox->path, "/cur/", fname, NULL);
178N/A ret = func(mbox, path, context);
178N/A t_pop();
178N/A return ret;
178N/A}
178N/A
178N/Aint maildir_file_do(struct maildir_mailbox *mbox, uint32_t uid,
178N/A maildir_file_do_func *func, void *context)
178N/A{
178N/A int i, ret;
178N/A
178N/A ret = maildir_file_do_try(mbox, uid, func, context);
178N/A for (i = 0; i < 10 && ret == 0; i++) {
178N/A /* file is either renamed or deleted. sync the maildir and
178N/A see which one. if file appears to be renamed constantly,
178N/A don't try to open it more than 10 times. */
178N/A if (maildir_storage_sync_force(mbox) < 0)
178N/A return -1;
178N/A
178N/A ret = maildir_file_do_try(mbox, uid, func, context);
178N/A }
178N/A
178N/A if (i == 10) {
178N/A ret = -1;
178N/A mail_storage_set_critical(STORAGE(mbox->storage),
178N/A "maildir_file_do(%s) racing", mbox->path);
178N/A }
178N/A
178N/A return ret == -2 ? 0 : ret;
178N/A}
178N/A
178N/Aconst char *maildir_generate_tmp_filename(const struct timeval *tv)
178N/A{
178N/A static unsigned int create_count = 0;
178N/A static time_t first_stamp = 0;
181N/A
178N/A if (first_stamp == 0 || first_stamp == ioloop_time) {
178N/A /* it's possible that within last second another process had
178N/A the same PID as us. Use usecs to make sure we don't create
178N/A duplicate base name. */
178N/A first_stamp = ioloop_time;
178N/A return t_strdup_printf("%s.P%sQ%uM%s.%s",
178N/A dec2str(tv->tv_sec), my_pid,
178N/A create_count++,
178N/A dec2str(tv->tv_usec), my_hostname);
178N/A } else {
178N/A /* Don't bother with usecs. Saves a bit space :) */
178N/A return t_strdup_printf("%s.P%sQ%u.%s",
178N/A dec2str(tv->tv_sec), my_pid,
178N/A create_count++, my_hostname);
}
}
int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
mode_t mode, const char **fname_r)
{
const char *path, *tmp_fname;
struct stat st;
struct timeval *tv, tv_now;
pool_t pool;
int fd;
tv = &ioloop_timeval;
pool = pool_alloconly_create("maildir_tmp", 4096);
for (;;) {
p_clear(pool);
tmp_fname = maildir_generate_tmp_filename(tv);
path = p_strconcat(pool, dir, "/", tmp_fname, NULL);
if (stat(path, &st) < 0 && errno == ENOENT) {
/* doesn't exist */
mode_t old_mask = umask(0);
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
umask(old_mask);
if (fd != -1 || errno != EEXIST)
break;
}
/* wait and try again - very unlikely */
sleep(2);
tv = &tv_now;
if (gettimeofday(&tv_now, NULL) < 0)
i_fatal("gettimeofday(): %m");
}
*fname_r = t_strdup(path);
if (fd == -1) {
if (ENOSPACE(errno)) {
mail_storage_set_error(STORAGE(mbox->storage),
"Not enough disk space");
} else {
mail_storage_set_critical(STORAGE(mbox->storage),
"open(%s) failed: %m", path);
}
}
pool_unref(pool);
return fd;
}
bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r)
{
uoff_t size = 0;
for (; *fname != '\0'; fname++) {
i_assert(*fname != '/');
if (*fname == ',' && fname[1] == type && fname[2] == '=') {
fname += 3;
break;
}
}
if (*fname == '\0')
return FALSE;
while (*fname >= '0' && *fname <= '9') {
size = size * 10 + (*fname - '0');
fname++;
}
if (*fname != MAILDIR_INFO_SEP &&
*fname != MAILDIR_EXTRA_SEP &&
*fname != '\0')
return FALSE;
*size_r = size;
return TRUE;
}
/* a char* hash function from ASU -- from glib */
unsigned int maildir_hash(const void *p)
{
const unsigned char *s = p;
unsigned int g, h = 0;
while (*s != MAILDIR_INFO_SEP && *s != '\0') {
i_assert(*s != '/');
h = (h << 4) + *s;
if ((g = h & 0xf0000000UL)) {
h = h ^ (g >> 24);
h = h ^ g;
}
s++;
}
return h;
}
int maildir_cmp(const void *p1, const void *p2)
{
const char *s1 = p1, *s2 = p2;
while (*s1 == *s2 && *s1 != MAILDIR_INFO_SEP && *s1 != '\0') {
i_assert(*s1 != '/');
s1++; s2++;
}
if ((*s1 == '\0' || *s1 == MAILDIR_INFO_SEP) &&
(*s2 == '\0' || *s2 == MAILDIR_INFO_SEP))
return 0;
return *s1 - *s2;
}