dict-file.c (dovecot-2.3.16) | : | dict-file.c (dovecot-2.3.17) | ||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
#include <unistd.h> | #include <unistd.h> | |||
#include <fcntl.h> | #include <fcntl.h> | |||
#include <sys/stat.h> | #include <sys/stat.h> | |||
struct file_dict { | struct file_dict { | |||
struct dict dict; | struct dict dict; | |||
pool_t hash_pool; | pool_t hash_pool; | |||
enum file_lock_method lock_method; | enum file_lock_method lock_method; | |||
char *path; | char *path; | |||
char *home_dir; | ||||
bool dict_path_checked; | ||||
HASH_TABLE(char *, char *) hash; | HASH_TABLE(char *, char *) hash; | |||
int fd; | int fd; | |||
bool refreshed; | bool refreshed; | |||
}; | }; | |||
struct file_dict_iterate_path { | ||||
const char *path; | ||||
size_t len; | ||||
}; | ||||
struct file_dict_iterate_context { | struct file_dict_iterate_context { | |||
struct dict_iterate_context ctx; | struct dict_iterate_context ctx; | |||
pool_t pool; | pool_t pool; | |||
struct hash_iterate_context *iter; | struct hash_iterate_context *iter; | |||
struct file_dict_iterate_path *paths; | const char *path; | |||
size_t path_len; | ||||
enum dict_iterate_flags flags; | enum dict_iterate_flags flags; | |||
const char *values[2]; | const char *values[2]; | |||
const char *error; | const char *error; | |||
}; | }; | |||
static struct dotlock_settings file_dict_dotlock_settings = { | static struct dotlock_settings file_dict_dotlock_settings = { | |||
.timeout = 60*2, | .timeout = 60*2, | |||
.stale_timeout = 60, | .stale_timeout = 60, | |||
.use_io_notify = TRUE | .use_io_notify = TRUE | |||
}; | }; | |||
static int | static int | |||
file_dict_ensure_path_home_dir(struct file_dict *dict, const char *home_dir, | ||||
const char **error_r) | ||||
{ | ||||
if (null_strcmp(dict->home_dir, home_dir) == 0) | ||||
return 0; | ||||
if (dict->dict_path_checked) { | ||||
*error_r = t_strdup_printf("home_dir changed from %s to %s " | ||||
"(requested dict was: %s)", dict->home_dir, | ||||
home_dir, dict->path); | ||||
return -1; | ||||
} | ||||
char *_p = dict->path; | ||||
dict->path = i_strdup(home_expand_tilde(dict->path, home_dir)); | ||||
dict->home_dir = i_strdup(home_dir); | ||||
i_free(_p); | ||||
dict->dict_path_checked = TRUE; | ||||
return 0; | ||||
} | ||||
static int | ||||
file_dict_init(struct dict *driver, const char *uri, | file_dict_init(struct dict *driver, const char *uri, | |||
const struct dict_settings *set, | const struct dict_settings *set ATTR_UNUSED, | |||
struct dict **dict_r, const char **error_r) | struct dict **dict_r, const char **error_r) | |||
{ | { | |||
struct file_dict *dict; | struct file_dict *dict; | |||
const char *p, *path; | const char *p, *path; | |||
dict = i_new(struct file_dict, 1); | dict = i_new(struct file_dict, 1); | |||
dict->lock_method = FILE_LOCK_METHOD_DOTLOCK; | dict->lock_method = FILE_LOCK_METHOD_DOTLOCK; | |||
p = strchr(uri, ':'); | p = strchr(uri, ':'); | |||
if (p == NULL) { | if (p == NULL) { | |||
skipping to change at line 86 | skipping to change at line 106 | |||
if (strcmp(p, "lock=fcntl") == 0) | if (strcmp(p, "lock=fcntl") == 0) | |||
dict->lock_method = FILE_LOCK_METHOD_FCNTL; | dict->lock_method = FILE_LOCK_METHOD_FCNTL; | |||
else if (strcmp(p, "lock=flock") == 0) | else if (strcmp(p, "lock=flock") == 0) | |||
dict->lock_method = FILE_LOCK_METHOD_FLOCK; | dict->lock_method = FILE_LOCK_METHOD_FLOCK; | |||
else { | else { | |||
*error_r = t_strdup_printf("Invalid parameter: %s", p+1); | *error_r = t_strdup_printf("Invalid parameter: %s", p+1); | |||
i_free(dict); | i_free(dict); | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
dict->path = set->home_dir == NULL ? i_strdup(path) : | ||||
i_strdup(home_expand_tilde(path, set->home_dir)); | /* keep the path for now, later in dict operations check if home_dir | |||
should be prepended. */ | ||||
dict->path = i_strdup(path); | ||||
dict->dict = *driver; | dict->dict = *driver; | |||
dict->hash_pool = pool_alloconly_create("file dict", 1024); | dict->hash_pool = pool_alloconly_create("file dict", 1024); | |||
hash_table_create(&dict->hash, dict->hash_pool, 0, str_hash, strcmp); | hash_table_create(&dict->hash, dict->hash_pool, 0, str_hash, strcmp); | |||
dict->fd = -1; | dict->fd = -1; | |||
*dict_r = &dict->dict; | *dict_r = &dict->dict; | |||
return 0; | return 0; | |||
} | } | |||
static void file_dict_deinit(struct dict *_dict) | static void file_dict_deinit(struct dict *_dict) | |||
{ | { | |||
struct file_dict *dict = (struct file_dict *)_dict; | struct file_dict *dict = (struct file_dict *)_dict; | |||
i_close_fd_path(&dict->fd, dict->path); | i_close_fd_path(&dict->fd, dict->path); | |||
hash_table_destroy(&dict->hash); | hash_table_destroy(&dict->hash); | |||
pool_unref(&dict->hash_pool); | pool_unref(&dict->hash_pool); | |||
i_free(dict->path); | i_free(dict->path); | |||
i_free(dict->home_dir); | ||||
i_free(dict); | i_free(dict); | |||
} | } | |||
static bool file_dict_need_refresh(struct file_dict *dict) | static bool file_dict_need_refresh(struct file_dict *dict) | |||
{ | { | |||
struct stat st1, st2; | struct stat st1, st2; | |||
if (dict->dict.iter_count > 0) { | if (dict->dict.iter_count > 0) { | |||
/* Change nothing while there are iterators or they can crash | /* Change nothing while there are iterators or they can crash | |||
because the hash table content recreated. */ | because the hash table content recreated. */ | |||
skipping to change at line 200 | skipping to change at line 224 | |||
value = str_tabunescape(p_strdup(dict->hash_pool, value)) ; | value = str_tabunescape(p_strdup(dict->hash_pool, value)) ; | |||
hash_table_update(dict->hash, key, value); | hash_table_update(dict->hash, key, value); | |||
} | } | |||
i_stream_destroy(&input); | i_stream_destroy(&input); | |||
} | } | |||
dict->refreshed = TRUE; | dict->refreshed = TRUE; | |||
return 0; | return 0; | |||
} | } | |||
static int file_dict_lookup(struct dict *_dict, pool_t pool, const char *key, | static int file_dict_lookup(struct dict *_dict, | |||
const struct dict_op_settings *set, | ||||
pool_t pool, const char *key, | ||||
const char **value_r, const char **error_r) | const char **value_r, const char **error_r) | |||
{ | { | |||
struct file_dict *dict = (struct file_dict *)_dict; | struct file_dict *dict = (struct file_dict *)_dict; | |||
if (file_dict_ensure_path_home_dir(dict, set->home_dir, error_r) < 0) | ||||
return -1; | ||||
if (file_dict_refresh(dict, error_r) < 0) | if (file_dict_refresh(dict, error_r) < 0) | |||
return -1; | return -1; | |||
*value_r = p_strdup(pool, hash_table_lookup(dict->hash, key)); | *value_r = p_strdup(pool, hash_table_lookup(dict->hash, key)); | |||
return *value_r == NULL ? 0 : 1; | return *value_r == NULL ? 0 : 1; | |||
} | } | |||
static struct dict_iterate_context * | static struct dict_iterate_context * | |||
file_dict_iterate_init(struct dict *_dict, const char *const *paths, | file_dict_iterate_init(struct dict *_dict, | |||
enum dict_iterate_flags flags) | const struct dict_op_settings *set ATTR_UNUSED, | |||
const char *path, enum dict_iterate_flags flags) | ||||
{ | { | |||
struct file_dict_iterate_context *ctx; | struct file_dict_iterate_context *ctx; | |||
struct file_dict *dict = (struct file_dict *)_dict; | struct file_dict *dict = (struct file_dict *)_dict; | |||
unsigned int i, path_count; | ||||
const char *error; | const char *error; | |||
pool_t pool; | pool_t pool; | |||
pool = pool_alloconly_create("file dict iterate", 256); | pool = pool_alloconly_create("file dict iterate", 256); | |||
ctx = p_new(pool, struct file_dict_iterate_context, 1); | ctx = p_new(pool, struct file_dict_iterate_context, 1); | |||
ctx->ctx.dict = _dict; | ctx->ctx.dict = _dict; | |||
ctx->pool = pool; | ctx->pool = pool; | |||
for (path_count = 0; paths[path_count] != NULL; path_count++) ; | ctx->path = p_strdup(pool, path); | |||
ctx->paths = p_new(pool, struct file_dict_iterate_path, path_count + 1); | ctx->path_len = strlen(path); | |||
for (i = 0; i < path_count; i++) { | ||||
ctx->paths[i].path = p_strdup(pool, paths[i]); | ||||
ctx->paths[i].len = strlen(paths[i]); | ||||
} | ||||
ctx->flags = flags; | ctx->flags = flags; | |||
if (file_dict_refresh(dict, &error) < 0) | if (file_dict_ensure_path_home_dir(dict, set->home_dir, &error) < 0 || | |||
file_dict_refresh(dict, &error) < 0) | ||||
ctx->error = p_strdup(pool, error); | ctx->error = p_strdup(pool, error); | |||
ctx->iter = hash_table_iterate_init(dict->hash); | ctx->iter = hash_table_iterate_init(dict->hash); | |||
return &ctx->ctx; | return &ctx->ctx; | |||
} | } | |||
static const struct file_dict_iterate_path * | static bool | |||
file_dict_iterate_find_path(struct file_dict_iterate_context *ctx, | file_dict_iterate_key_matches(struct file_dict_iterate_context *ctx, | |||
const char *key) | const char *key) | |||
{ | { | |||
unsigned int i; | if (strncmp(ctx->path, key, ctx->path_len) == 0) | |||
return TRUE; | ||||
for (i = 0; ctx->paths[i].path != NULL; i++) { | return FALSE; | |||
if (strncmp(ctx->paths[i].path, key, ctx->paths[i].len) == 0) | ||||
return &ctx->paths[i]; | ||||
} | ||||
return NULL; | ||||
} | } | |||
static bool file_dict_iterate(struct dict_iterate_context *_ctx, | static bool file_dict_iterate(struct dict_iterate_context *_ctx, | |||
const char **key_r, const char *const **values_r) | const char **key_r, const char *const **values_r) | |||
{ | { | |||
struct file_dict_iterate_context *ctx = | struct file_dict_iterate_context *ctx = | |||
(struct file_dict_iterate_context *)_ctx; | (struct file_dict_iterate_context *)_ctx; | |||
const struct file_dict_iterate_path *path; | ||||
char *key, *value; | char *key, *value; | |||
while (hash_table_iterate(ctx->iter, | while (hash_table_iterate(ctx->iter, | |||
((struct file_dict *)_ctx->dict)->hash, | ((struct file_dict *)_ctx->dict)->hash, | |||
&key, &value)) { | &key, &value)) { | |||
path = file_dict_iterate_find_path(ctx, key); | if (!file_dict_iterate_key_matches(ctx, key)) | |||
if (path == NULL) | ||||
continue; | continue; | |||
if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0) { | if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0) { | |||
/* match everything */ | /* match everything */ | |||
} else if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) { | } else if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) { | |||
if (key[path->len] != '\0') | if (key[ctx->path_len] != '\0') | |||
continue; | continue; | |||
} else { | } else { | |||
if (strchr(key + path->len, '/') != NULL) | if (strchr(key + ctx->path_len, '/') != NULL) | |||
continue; | continue; | |||
} | } | |||
*key_r = key; | *key_r = key; | |||
ctx->values[0] = value; | ctx->values[0] = value; | |||
*values_r = ctx->values; | *values_r = ctx->values; | |||
return TRUE; | return TRUE; | |||
} | } | |||
return FALSE; | return FALSE; | |||
} | } | |||
skipping to change at line 502 | skipping to change at line 522 | |||
"creat(%s) failed: %m", dict->path); | "creat(%s) failed: %m", dict->path); | |||
} | } | |||
return -1; | return -1; | |||
} | } | |||
if (fd_copy_parent_dir_permissions(dict->path, dict->fd, | if (fd_copy_parent_dir_permissions(dict->path, dict->fd, | |||
dict->path, &error) < 0) | dict->path, &error) < 0) | |||
e_error(dict->dict.event, "%s", error); | e_error(dict->dict.event, "%s", error); | |||
} | } | |||
*lock_r = NULL; | *lock_r = NULL; | |||
struct file_lock_settings lock_set = { | ||||
.lock_method = dict->lock_method, | ||||
}; | ||||
do { | do { | |||
file_lock_free(lock_r); | file_lock_free(lock_r); | |||
if (file_wait_lock(dict->fd, dict->path, F_WRLCK, | if (file_wait_lock(dict->fd, dict->path, F_WRLCK, &lock_set, | |||
dict->lock_method, | ||||
file_dict_dotlock_settings.timeout, | file_dict_dotlock_settings.timeout, | |||
lock_r) <= 0) { | lock_r, &error) <= 0) { | |||
*error_r = t_strdup_printf( | *error_r = t_strdup_printf( | |||
"file_wait_lock(%s) failed: %m", dict->path); | "file_wait_lock(%s) failed: %s", | |||
dict->path, error); | ||||
return -1; | return -1; | |||
} | } | |||
/* check again if we need to reopen the file because it was | /* check again if we need to reopen the file because it was | |||
just replaced */ | just replaced */ | |||
} while ((ret = file_dict_open_latest(dict, error_r)) > 0); | } while ((ret = file_dict_open_latest(dict, error_r)) > 0); | |||
return ret < 0 ? -1 : 0; | return ret < 0 ? -1 : 0; | |||
} | } | |||
static int | static int | |||
skipping to change at line 536 | skipping to change at line 559 | |||
const char *temp_path = NULL; | const char *temp_path = NULL; | |||
const char *error; | const char *error; | |||
struct hash_iterate_context *iter; | struct hash_iterate_context *iter; | |||
struct ostream *output; | struct ostream *output; | |||
char *key, *value; | char *key, *value; | |||
string_t *str; | string_t *str; | |||
int fd = -1; | int fd = -1; | |||
*atomic_inc_not_found_r = FALSE; | *atomic_inc_not_found_r = FALSE; | |||
if (file_dict_ensure_path_home_dir(dict, ctx->ctx.set.home_dir, error_r) | ||||
< 0) | ||||
return -1; | ||||
switch (dict->lock_method) { | switch (dict->lock_method) { | |||
case FILE_LOCK_METHOD_FCNTL: | case FILE_LOCK_METHOD_FCNTL: | |||
case FILE_LOCK_METHOD_FLOCK: | case FILE_LOCK_METHOD_FLOCK: | |||
if (file_dict_lock(dict, &lock, error_r) < 0) | if (file_dict_lock(dict, &lock, error_r) < 0) | |||
return -1; | return -1; | |||
temp_path = t_strdup_printf("%s.tmp", dict->path); | temp_path = t_strdup_printf("%s.tmp", dict->path); | |||
fd = creat(temp_path, 0600); | fd = creat(temp_path, 0600); | |||
if (fd == -1) { | if (fd == -1) { | |||
*error_r = t_strdup_printf( | *error_r = t_strdup_printf( | |||
"dict-file: creat(%s) failed: %m", temp_path); | "dict-file: creat(%s) failed: %m", temp_path); | |||
End of changes. 24 change blocks. | ||||
39 lines changed or deleted | 66 lines changed or added |