Mercurial > dovecot > original-hg > dovecot-1.2
view src/deliver/duplicate.c @ 6475:ee420f238104 HEAD
Renamed hash_size() -> hash_count().
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 22 Sep 2007 19:03:33 +0300 |
parents | 65c69a53a7be |
children | f74e84fd11fc |
line wrap: on
line source
/* Copyright (c) 2005-2007 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "home-expand.h" #include "file-dotlock.h" #include "hash.h" #include "duplicate.h" #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #define DUPLICATE_PATH "~/.dovecot.lda-dupes" #define COMPRESS_PERCENTAGE 10 #define DUPLICATE_BUFSIZE 4096 struct duplicate { const void *id; unsigned int id_size; const char *user; time_t time; }; struct duplicate_file { pool_t pool; struct hash_table *hash; const char *path; int new_fd; struct dotlock *dotlock; unsigned int changed:1; }; static struct dotlock_settings duplicate_dotlock_set = { MEMBER(temp_prefix) NULL, MEMBER(lock_suffix) NULL, MEMBER(timeout) 10, MEMBER(stale_timeout) 60, MEMBER(callback) NULL, MEMBER(context) NULL, MEMBER(use_excl_lock) FALSE }; static struct duplicate_file *duplicate_file = NULL; static int duplicate_cmp(const void *p1, const void *p2) { const struct duplicate *d1 = p1, *d2 = p2; return (d1->id_size == d2->id_size && memcmp(d1->id, d2->id, d1->id_size) == 0 && strcasecmp(d1->user, d2->user) == 0) ? 0 : 1; } static unsigned int duplicate_hash(const void *p) { /* a char* hash function from ASU -- from glib */ const struct duplicate *d = p; const unsigned char *s = d->id, *end = s + d->id_size; unsigned int g, h = 0; while (s != end) { h = (h << 4) + *s; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h ^ strcase_hash(d->user); } static int duplicate_read(struct duplicate_file *file) { int fd; struct istream *input; const unsigned char *data; size_t size; time_t stamp; unsigned int offset, id_size, user_size, change_count; bool broken = FALSE; fd = open(file->path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", file->path); return -1; } /* <timestamp> <id_size> <user_size> <id> <user> */ input = i_stream_create_fd(fd, DUPLICATE_BUFSIZE, FALSE); change_count = 0; while (i_stream_read_data(input, &data, &size, sizeof(stamp) + sizeof(id_size) + sizeof(user_size)) > 0) { offset = 0; memcpy(&stamp, data, sizeof(stamp)); offset += sizeof(stamp); memcpy(&id_size, data + offset, sizeof(id_size)); offset += sizeof(id_size); memcpy(&user_size, data + offset, sizeof(user_size)); offset += sizeof(user_size); i_stream_skip(input, offset); if (id_size == 0 || user_size == 0 || id_size > DUPLICATE_BUFSIZE || user_size > DUPLICATE_BUFSIZE) { i_error("broken duplicate file %s", file->path); broken = TRUE; break; } if (i_stream_read_data(input, &data, &size, id_size + user_size - 1) <= 0) { i_error("unexpected end of file in %s", file->path); broken = TRUE; break; } if (stamp >= ioloop_time) { /* still valid, save it */ struct duplicate *d; void *new_id; new_id = p_malloc(file->pool, id_size); memcpy(new_id, data, id_size); d = p_new(file->pool, struct duplicate, 1); d->id = new_id; d->id_size = id_size; d->user = p_strndup(file->pool, data + id_size, user_size); d->time = stamp; hash_insert(file->hash, d, d); } else { change_count++; } i_stream_skip(input, id_size + user_size); } if (hash_count(file->hash) * COMPRESS_PERCENTAGE / 100 > change_count) file->changed = TRUE; i_stream_unref(&input); if (close(fd) < 0) i_error("close(%s) failed: %m", file->path); if (broken) { if (unlink(file->path) < 0 && errno != ENOENT) i_error("unlink(%s) failed: %m", file->path); } return 0; } static struct duplicate_file *duplicate_new(const char *path) { struct duplicate_file *file; pool_t pool; pool = pool_alloconly_create("duplicates", 10240); file = p_new(pool, struct duplicate_file, 1); file->pool = pool; file->path = p_strdup(pool, path); file->new_fd = file_dotlock_open(&duplicate_dotlock_set, path, 0, &file->dotlock); file->hash = hash_create(default_pool, pool, 0, duplicate_hash, duplicate_cmp); (void)duplicate_read(file); return file; } static void duplicate_free(struct duplicate_file *file) { if (file->dotlock != NULL) file_dotlock_delete(&file->dotlock); hash_destroy(&file->hash); pool_unref(&file->pool); } int duplicate_check(const void *id, size_t id_size, const char *user) { struct duplicate d; if (duplicate_file == NULL) duplicate_file = duplicate_new(home_expand(DUPLICATE_PATH)); d.id = id; d.id_size = id_size; d.user = user; return hash_lookup(duplicate_file->hash, &d) != NULL; } void duplicate_mark(const void *id, size_t id_size, const char *user, time_t time) { struct duplicate *d; void *new_id; if (duplicate_file == NULL) duplicate_file = duplicate_new(home_expand(DUPLICATE_PATH)); new_id = p_malloc(duplicate_file->pool, id_size); memcpy(new_id, id, id_size); d = p_new(duplicate_file->pool, struct duplicate, 1); d->id = new_id; d->id_size = id_size; d->user = p_strdup(duplicate_file->pool, user); d->time = time; duplicate_file->changed = TRUE; hash_insert(duplicate_file->hash, d, d); } void duplicate_flush(void) { struct duplicate_file *file = duplicate_file; struct ostream *output; struct hash_iterate_context *iter; void *key, *value; if (duplicate_file == NULL || !file->changed || file->new_fd == -1) return; output = o_stream_create_fd_file(file->new_fd, 0, FALSE); iter = hash_iterate_init(file->hash); while (hash_iterate(iter, &key, &value)) { struct duplicate *d = value; unsigned int user_size = strlen(d->user); o_stream_send(output, &d->time, sizeof(d->time)); o_stream_send(output, &d->id_size, sizeof(d->id_size)); o_stream_send(output, &user_size, sizeof(user_size)); o_stream_send(output, d->id, d->id_size); o_stream_send(output, d->user, user_size); } hash_iterate_deinit(&iter); o_stream_unref(&output); file->changed = FALSE; if (file_dotlock_replace(&file->dotlock, 0) < 0) i_error("file_dotlock_replace(%s) failed: %m", file->path); file->new_fd = -1; } void duplicate_init(void) { duplicate_dotlock_set.use_excl_lock = getenv("DOTLOCK_USE_EXCL") != NULL; } void duplicate_deinit(void) { if (duplicate_file != NULL) { duplicate_flush(); duplicate_free(duplicate_file); } }