dict-file.c revision c73415e93ecf1c699ef054d2b179b10976fa23f3
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2008-2012 Dovecot authors, see the included COPYING file */
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "lib.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "array.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "hash.h"
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen#include "file-lock.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "file-dotlock.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "nfs-workarounds.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "istream.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "ostream.h"
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen#include "dict-transaction-memory.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include "dict-private.h"
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen#include <stdio.h>
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include <stdlib.h>
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include <unistd.h>
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include <fcntl.h>
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen#include <sys/stat.h>
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstruct file_dict {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct dict dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen pool_t hash_pool;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen enum file_lock_method lock_method;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen char *path;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE(char *, char *) hash;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen int fd;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen bool refreshed;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen};
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenstruct file_dict_iterate_path {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen const char *path;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int len;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen};
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstruct file_dict_iterate_context {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct dict_iterate_context ctx;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen pool_t pool;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct hash_iterate_context *iter;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen struct file_dict_iterate_path *paths;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen enum dict_iterate_flags flags;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen unsigned int failed:1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen};
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic struct dotlock_settings file_dict_dotlock_settings = {
f323e3f0de9841f399aba5919e3f25652a88fa65Timo Sirainen .timeout = 60*2,
f323e3f0de9841f399aba5919e3f25652a88fa65Timo Sirainen .stale_timeout = 60,
f323e3f0de9841f399aba5919e3f25652a88fa65Timo Sirainen .use_io_notify = TRUE
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen};
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
10399559650f552a23949772be79eb6a80198c5aTimo Sirainenstatic int
10399559650f552a23949772be79eb6a80198c5aTimo Sirainenfile_dict_init(struct dict *driver, const char *uri,
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen enum dict_data_type value_type ATTR_UNUSED,
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen const char *username ATTR_UNUSED,
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char *base_dir ATTR_UNUSED, struct dict **dict_r,
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char **error_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict;
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen const char *p;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict = i_new(struct file_dict, 1);
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen dict->lock_method = FILE_LOCK_METHOD_DOTLOCK;
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen p = strchr(uri, ':');
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen if (p == NULL) {
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen /* no parameters */
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen dict->path = i_strdup(uri);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen } else {
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen dict->path = i_strdup_until(uri, p++);
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen if (strcmp(p, "lock=fcntl") == 0)
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen dict->lock_method = FILE_LOCK_METHOD_FCNTL;
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen else if (strcmp(p, "lock=flock") == 0)
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen dict->lock_method = FILE_LOCK_METHOD_FLOCK;
8f2eb1ee9ec07661bd50275da99b5f351972a49aTimo Sirainen else {
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen *error_r = t_strdup_printf("Invalid parameter: %s", p+1);
8f2eb1ee9ec07661bd50275da99b5f351972a49aTimo Sirainen i_free(dict->path);
8f2eb1ee9ec07661bd50275da99b5f351972a49aTimo Sirainen i_free(dict);
8f2eb1ee9ec07661bd50275da99b5f351972a49aTimo Sirainen return -1;
8f2eb1ee9ec07661bd50275da99b5f351972a49aTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict->dict = *driver;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict->hash_pool = pool_alloconly_create("file dict", 1024);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&dict->hash, dict->hash_pool, 0, str_hash, strcmp);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict->fd = -1;
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen *dict_r = &dict->dict;
dc599de6096c51e6c922e069bfbbcb7d68c50ffaStephan Bosch return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic void file_dict_deinit(struct dict *_dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
add4be35a949fcfa67da15faf64ee0e8c163e648Timo Sirainen if (dict->fd != -1) {
add4be35a949fcfa67da15faf64ee0e8c163e648Timo Sirainen if (close(dict->fd) < 0)
add4be35a949fcfa67da15faf64ee0e8c163e648Timo Sirainen i_error("close(%s) failed: %m", dict->path);
add4be35a949fcfa67da15faf64ee0e8c163e648Timo Sirainen }
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_destroy(&dict->hash);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen pool_unref(&dict->hash_pool);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_free(dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_free(dict);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic bool file_dict_need_refresh(struct file_dict *dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct stat st1, st2;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (dict->fd == -1)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen nfs_flush_file_handle_cache(dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (nfs_safe_stat(dict->path, &st1) < 0) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_error("stat(%s) failed: %m", dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return FALSE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (fstat(dict->fd, &st2) < 0) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (errno != ESTALE)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_error("fstat(%s) failed: %m", dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (st1.st_ino != st2.st_ino ||
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen !CMP_DEV_T(st1.st_dev, st2.st_dev)) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* file changed */
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return FALSE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenstatic int file_dict_open_latest(struct file_dict *dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen int open_type;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (!file_dict_need_refresh(dict))
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (dict->fd != -1) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (close(dict->fd) < 0)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_error("close(%s) failed: %m", dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen open_type = dict->lock_method == FILE_LOCK_METHOD_DOTLOCK ?
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen O_RDONLY : O_RDWR;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen dict->fd = open(dict->path, open_type);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (dict->fd == -1) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (errno == ENOENT)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen i_error("open(%s) failed: %m", dict->path);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return -1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen dict->refreshed = FALSE;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return 1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen}
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenstatic int file_dict_refresh(struct file_dict *dict)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen{
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen struct istream *input;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen char *key, *value;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_dict_open_latest(dict) < 0)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (dict->refreshed)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_clear(dict->hash, TRUE);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen p_clear(dict->hash_pool);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen if (dict->fd != -1) {
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen input = i_stream_create_fd(dict->fd, (size_t)-1, FALSE);
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen while ((key = i_stream_read_next_line(input)) != NULL) {
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen /* strdup() before the second read */
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen key = p_strdup(dict->hash_pool, key);
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen if ((value = i_stream_read_next_line(input)) == NULL)
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen break;
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen value = p_strdup(dict->hash_pool, value);
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen hash_table_insert(dict->hash, key, value);
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen }
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen i_stream_destroy(&input);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen dict->refreshed = TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic int file_dict_lookup(struct dict *_dict, pool_t pool,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen const char *key, const char **value_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (file_dict_refresh(dict) < 0)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return -1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen *value_r = p_strdup(pool, hash_table_lookup(dict->hash, key));
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return *value_r == NULL ? 0 : 1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic struct dict_iterate_context *
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenfile_dict_iterate_init(struct dict *_dict, const char *const *paths,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen enum dict_iterate_flags flags)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict_iterate_context *ctx;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int i, path_count;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen pool_t pool;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen pool = pool_alloconly_create("file dict iterate", 256);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx = p_new(pool, struct file_dict_iterate_context, 1);
8ad53e0bb29f61350f608fc519210f2442c20775Timo Sirainen ctx->ctx.dict = _dict;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->pool = pool;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (path_count = 0; paths[path_count] != NULL; path_count++) ;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->paths = p_new(pool, struct file_dict_iterate_path, path_count + 1);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (i = 0; i < path_count; i++) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->paths[i].path = p_strdup(pool, paths[i]);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->paths[i].len = strlen(paths[i]);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen ctx->flags = flags;
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen ctx->iter = hash_table_iterate_init(dict->hash);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (file_dict_refresh(dict) < 0)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen ctx->failed = TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return &ctx->ctx;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenstatic const struct file_dict_iterate_path *
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenfile_dict_iterate_find_path(struct file_dict_iterate_context *ctx,
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen const char *key)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen{
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int i;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (i = 0; ctx->paths[i].path != NULL; i++) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (strncmp(ctx->paths[i].path, key, ctx->paths[i].len) == 0)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen return &ctx->paths[i];
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen }
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen return NULL;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen}
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainenstatic bool file_dict_iterate(struct dict_iterate_context *_ctx,
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen const char **key_r, const char **value_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict_iterate_context *ctx =
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen (struct file_dict_iterate_context *)_ctx;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen const struct file_dict_iterate_path *path;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *key, *value;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
c73415e93ecf1c699ef054d2b179b10976fa23f3Timo Sirainen while (hash_table_iterate(ctx->iter,
c73415e93ecf1c699ef054d2b179b10976fa23f3Timo Sirainen ((struct file_dict *)_ctx->dict)->hash,
c73415e93ecf1c699ef054d2b179b10976fa23f3Timo Sirainen &key, &value)) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen path = file_dict_iterate_find_path(ctx, key);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (path == NULL)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen continue;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 &&
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen strchr(key + path->len, '/') != NULL)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen continue;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen *key_r = key;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen *value_r = value;
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen return TRUE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen return FALSE;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainenstatic int file_dict_iterate_deinit(struct dict_iterate_context *_ctx)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict_iterate_context *ctx =
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen (struct file_dict_iterate_context *)_ctx;
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen int ret = ctx->failed ? -1 : 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_iterate_deinit(&ctx->iter);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen pool_unref(&ctx->pool);
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen return ret;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic struct dict_transaction_context *
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenfile_dict_transaction_init(struct dict *_dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen struct dict_transaction_memory_context *ctx;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen pool_t pool;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
dba8af1faaf9fd3957254bb6f2234b285f77096fTimo Sirainen pool = pool_alloconly_create("file dict transaction", 2048);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen ctx = p_new(pool, struct dict_transaction_memory_context, 1);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_init(ctx, _dict, pool);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return &ctx->ctx;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainenstatic void file_dict_apply_changes(struct dict_transaction_memory_context *ctx,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen bool *atomic_inc_not_found_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen const char *tmp;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen char *key, *value, *old_value;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *orig_key, *orig_value;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen const struct dict_transaction_memory_change *change;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen unsigned int new_len;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen long long diff;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen array_foreach(&ctx->changes, change) {
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen if (hash_table_lookup_full(dict->hash, change->key,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen &orig_key, &orig_value)) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen key = orig_key;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen old_value = orig_value;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen } else {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen key = NULL;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen old_value = NULL;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen value = NULL;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen switch (change->type) {
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen case DICT_CHANGE_TYPE_INC:
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen if (old_value == NULL) {
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen *atomic_inc_not_found_r = TRUE;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen break;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen }
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen diff = strtoll(old_value, NULL, 10) +
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen change->value.diff;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen tmp = t_strdup_printf("%lld", diff);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen new_len = strlen(tmp);
3c63c219ae7854b4f1d44a671a65572aa242cbcfTimo Sirainen if (old_value == NULL || new_len > strlen(old_value))
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen value = p_strdup(dict->hash_pool, tmp);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen else {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen memcpy(old_value, tmp, new_len + 1);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen value = old_value;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* fall through */
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen case DICT_CHANGE_TYPE_SET:
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (key == NULL)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen key = p_strdup(dict->hash_pool, change->key);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (value == NULL) {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen value = p_strdup(dict->hash_pool,
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen change->value.str);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_update(dict->hash, key, value);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen break;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen case DICT_CHANGE_TYPE_APPEND:
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen if (key == NULL)
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen key = p_strdup(dict->hash_pool, change->key);
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen if (old_value == NULL) {
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen value = p_strdup(dict->hash_pool,
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen change->value.str);
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen } else {
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen value = p_strconcat(dict->hash_pool, old_value,
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen change->value.str, NULL);
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen }
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen hash_table_update(dict->hash, key, value);
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen break;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen case DICT_CHANGE_TYPE_UNSET:
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (old_value != NULL)
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_remove(dict->hash, key);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen break;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenstatic int
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenfd_copy_stat_permissions(const struct stat *src_st,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen int dest_fd, const char *dest_path)
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen{
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen struct stat dest_st;
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen if (fstat(dest_fd, &dest_st) < 0) {
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen i_error("fstat(%s) failed: %m", dest_path);
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen return -1;
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (src_st->st_gid != dest_st.st_gid &&
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen ((src_st->st_mode & 0070) >> 3 != (src_st->st_mode & 0007))) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen /* group has different permissions from world.
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen preserve the group. */
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (fchown(dest_fd, (uid_t)-1, src_st->st_gid) < 0) {
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen i_error("fchown(%s, -1, %s) failed: %m",
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen dest_path, dec2str(src_st->st_gid));
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen return -1;
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if ((src_st->st_mode & 07777) != (dest_st.st_mode & 07777)) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (fchmod(dest_fd, src_st->st_mode & 07777) < 0) {
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen i_error("fchmod(%s, %o) failed: %m",
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen dest_path, (int)(src_st->st_mode & 0777));
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen return -1;
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen return 0;
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen}
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenstatic int fd_copy_permissions(int src_fd, const char *src_path,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen int dest_fd, const char *dest_path)
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen{
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen struct stat src_st;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (fstat(src_fd, &src_st) < 0) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen i_error("fstat(%s) failed: %m", src_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return -1;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen }
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen}
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenstatic int
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenfd_copy_parent_dir_permissions(const char *src_path, int dest_fd,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen const char *dest_path)
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen{
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen struct stat src_st;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen const char *src_dir, *p;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen p = strrchr(src_path, '/');
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (p == NULL)
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen src_dir = ".";
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen else
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen src_dir = t_strdup_until(src_path, p);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (stat(src_dir, &src_st) < 0) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen i_error("stat(%s) failed: %m", src_dir);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return -1;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen }
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen src_st.st_mode &= 0666;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen}
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenstatic int
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenfile_dict_lock(struct file_dict *dict, struct file_lock **lock_r)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen{
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen int ret;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_dict_open_latest(dict) < 0)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (dict->fd == -1) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen /* quota file doesn't exist yet, we need to create it */
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600);
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen if (dict->fd == -1) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("creat(%s) failed: %m", dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen (void)fd_copy_parent_dir_permissions(dict->path, dict->fd,
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen do {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_wait_lock(dict->fd, dict->path, F_WRLCK,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen dict->lock_method,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen file_dict_dotlock_settings.timeout,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen lock_r) <= 0) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file_wait_lock(%s) failed: %m", dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen /* check again if we need to reopen the file because it was
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen just replaced */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen } while ((ret = file_dict_open_latest(dict)) > 0);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return ret < 0 ? -1 : 0;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen}
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainenstatic int file_dict_write_changes(struct dict_transaction_memory_context *ctx,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen bool *atomic_inc_not_found_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen struct dotlock *dotlock = NULL;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen struct file_lock *lock = NULL;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen const char *temp_path = NULL;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct hash_iterate_context *iter;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct ostream *output;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *key, *value;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen int fd = -1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen *atomic_inc_not_found_r = FALSE;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen switch (dict->lock_method) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen case FILE_LOCK_METHOD_FCNTL:
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen case FILE_LOCK_METHOD_FLOCK:
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_dict_lock(dict, &lock) < 0)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path = t_strdup_printf("%s.tmp", dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen fd = creat(temp_path, 0600);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (fd == -1) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file dict commit: creat(%s) failed: %m",
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen break;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen &dotlock);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (fd == -1) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file dict commit: file_dotlock_open(%s) failed: %m",
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path = file_dotlock_get_lock_path(dotlock);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen break;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* refresh once more now that we're locked */
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (file_dict_refresh(dict) < 0) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (dotlock != NULL)
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen file_dotlock_delete(&dotlock);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen else {
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen file_unlock(&lock);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return -1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen if (dict->fd != -1) {
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen /* preserve the permissions */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen (void)fd_copy_permissions(dict->fd, dict->path, fd, temp_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen } else {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen /* get initial permissions from parent directory */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen (void)fd_copy_parent_dir_permissions(dict->path, fd, temp_path);
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen }
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen file_dict_apply_changes(ctx, atomic_inc_not_found_r);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen output = o_stream_create_fd(fd, 0, FALSE);
313c43343f711f04a8d4d1f8dd21aaab5c16b2d2Timo Sirainen o_stream_cork(output);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen iter = hash_table_iterate_init(dict->hash);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen while (hash_table_iterate(iter, dict->hash, &key, &value)) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, key);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, "\n", 1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, value);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, "\n", 1);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_iterate_deinit(&iter);
efd9d73a7fba2830431aa1186fd65372f6631399Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (o_stream_nfinish(output) < 0) {
efd9d73a7fba2830431aa1186fd65372f6631399Timo Sirainen i_error("write(%s) failed: %m", temp_path);
efd9d73a7fba2830431aa1186fd65372f6631399Timo Sirainen o_stream_destroy(&output);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
efd9d73a7fba2830431aa1186fd65372f6631399Timo Sirainen return -1;
efd9d73a7fba2830431aa1186fd65372f6631399Timo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen o_stream_destroy(&output);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (dotlock != NULL) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_dotlock_replace(&dotlock,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen } else {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (rename(temp_path, dict->path) < 0) {
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("rename(%s, %s) failed: %m",
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path, dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen file_unlock(&lock);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen return -1;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen }
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen file_lock_free(&lock);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if (dict->fd != -1)
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&dict->fd);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict->fd = fd;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return 0;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenstatic int
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenfile_dict_transaction_commit(struct dict_transaction_context *_ctx,
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen bool async ATTR_UNUSED,
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen dict_transaction_commit_callback_t *callback,
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen void *context)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen struct dict_transaction_memory_context *ctx =
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen (struct dict_transaction_memory_context *)_ctx;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen bool atomic_inc_not_found;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen int ret;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen if (file_dict_write_changes(ctx, &atomic_inc_not_found) < 0)
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen ret = -1;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen else if (atomic_inc_not_found)
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen ret = 0;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen else
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen ret = 1;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen pool_unref(&ctx->pool);
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen if (callback != NULL)
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen callback(ret, context);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen return ret;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstruct dict dict_driver_file = {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .name = "file",
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_init,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_deinit,
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen NULL,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_lookup,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_iterate_init,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_iterate,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_iterate_deinit,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_transaction_init,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen file_dict_transaction_commit,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_rollback,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_set,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_unset,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_append,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_atomic_inc
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen }
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen};