/src/dovecot/src/lib-index/mail-index.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "array.h" |
6 | | #include "buffer.h" |
7 | | #include "eacces-error.h" |
8 | | #include "hash.h" |
9 | | #include "str-sanitize.h" |
10 | | #include "mmap-util.h" |
11 | | #include "nfs-workarounds.h" |
12 | | #include "read-full.h" |
13 | | #include "write-full.h" |
14 | | #include "mail-index-alloc-cache.h" |
15 | | #include "mail-index-private.h" |
16 | | #include "mail-index-view-private.h" |
17 | | #include "mail-index-sync-private.h" |
18 | | #include "mail-index-modseq.h" |
19 | | #include "mail-transaction-log-private.h" |
20 | | #include "mail-transaction-log-view-private.h" |
21 | | #include "mail-cache.h" |
22 | | |
23 | | #include <stdio.h> |
24 | | #include <time.h> |
25 | | #include <sys/stat.h> |
26 | | #include <ctype.h> |
27 | | |
28 | | struct mail_index_module_register mail_index_module_register = { 0 }; |
29 | | |
30 | | struct event_category event_category_mail_index = { |
31 | | .name = "mail-index", |
32 | | }; |
33 | | |
34 | | static void mail_index_close_nonopened(struct mail_index *index); |
35 | | |
36 | | static const struct mail_index_optimization_settings default_optimization_set = { |
37 | | .index = { |
38 | | .rewrite_min_log_bytes = 8 * 1024, |
39 | | .rewrite_max_log_bytes = 128 * 1024, |
40 | | }, |
41 | | .log = { |
42 | | .min_size = 32 * 1024, |
43 | | .max_size = 1024 * 1024, |
44 | | .min_age_secs = 5 * 60, |
45 | | .log2_max_age_secs = 3600 * 24 * 2, |
46 | | }, |
47 | | .cache = { |
48 | | .unaccessed_field_drop_secs = 3600 * 24 * 30, |
49 | | .record_max_size = 64 * 1024, |
50 | | .max_size = 1024 * 1024 * 1024, |
51 | | .purge_min_size = 32 * 1024, |
52 | | .purge_delete_percentage = 20, |
53 | | .purge_continued_percentage = 200, |
54 | | .purge_header_continue_count = 4, |
55 | | }, |
56 | | }; |
57 | | |
58 | | struct mail_index *mail_index_alloc(struct event *parent_event, |
59 | | const char *dir, const char *prefix) |
60 | 0 | { |
61 | 0 | struct mail_index *index; |
62 | |
|
63 | 0 | index = i_new(struct mail_index, 1); |
64 | 0 | index->dir = i_strdup(dir); |
65 | 0 | index->prefix = i_strdup(prefix); |
66 | 0 | index->fd = -1; |
67 | 0 | index->event = event_create(parent_event); |
68 | 0 | event_add_category(index->event, &event_category_mail_index); |
69 | |
|
70 | 0 | index->extension_pool = |
71 | 0 | pool_alloconly_create(MEMPOOL_GROWING"index extension", 1024); |
72 | 0 | p_array_init(&index->extensions, index->extension_pool, 5); |
73 | 0 | i_array_init(&index->module_contexts, |
74 | 0 | I_MIN(5, mail_index_module_register.id)); |
75 | |
|
76 | 0 | index->set.mode = 0600; |
77 | 0 | index->set.gid = (gid_t)-1; |
78 | 0 | index->set.lock_method = FILE_LOCK_METHOD_FCNTL; |
79 | 0 | index->set.max_lock_timeout_secs = UINT_MAX; |
80 | 0 | index->optimization_set = default_optimization_set; |
81 | |
|
82 | 0 | index->keywords_ext_id = |
83 | 0 | mail_index_ext_register(index, MAIL_INDEX_EXT_KEYWORDS, |
84 | 0 | 128, 2, 1); |
85 | 0 | index->keywords_pool = pool_alloconly_create("keywords", 512); |
86 | 0 | i_array_init(&index->keywords, 16); |
87 | 0 | hash_table_create(&index->keywords_hash, index->keywords_pool, 0, |
88 | 0 | strcase_hash, strcasecmp); |
89 | 0 | index->log = mail_transaction_log_alloc(index); |
90 | 0 | mail_index_modseq_init(index); |
91 | 0 | return index; |
92 | 0 | } |
93 | | |
94 | | void mail_index_free(struct mail_index **_index) |
95 | 0 | { |
96 | 0 | struct mail_index *index = *_index; |
97 | |
|
98 | 0 | *_index = NULL; |
99 | |
|
100 | 0 | i_assert(index->open_count == 0); |
101 | | |
102 | 0 | mail_transaction_log_free(&index->log); |
103 | 0 | hash_table_destroy(&index->keywords_hash); |
104 | 0 | pool_unref(&index->extension_pool); |
105 | 0 | pool_unref(&index->keywords_pool); |
106 | |
|
107 | 0 | array_free(&index->keywords); |
108 | 0 | array_free(&index->module_contexts); |
109 | |
|
110 | 0 | event_unref(&index->event); |
111 | 0 | i_free(index->set.cache_dir); |
112 | 0 | i_free(index->set.ext_hdr_init_data); |
113 | 0 | i_free(index->set.gid_origin); |
114 | 0 | i_free(index->last_error.text); |
115 | 0 | i_free(index->dir); |
116 | 0 | i_free(index->prefix); |
117 | 0 | i_free(index->need_recreate); |
118 | 0 | i_free(index); |
119 | 0 | } |
120 | | |
121 | | void mail_index_set_cache_dir(struct mail_index *index, const char *dir) |
122 | 0 | { |
123 | 0 | i_free(index->set.cache_dir); |
124 | 0 | index->set.cache_dir = i_strdup(dir); |
125 | 0 | } |
126 | | |
127 | | void mail_index_set_fsync_mode(struct mail_index *index, |
128 | | enum fsync_mode mode, |
129 | | enum mail_index_fsync_mask mask) |
130 | 0 | { |
131 | 0 | index->set.fsync_mode = mode; |
132 | 0 | index->set.fsync_mask = mask; |
133 | 0 | } |
134 | | |
135 | | bool mail_index_use_existing_permissions(struct mail_index *index) |
136 | 0 | { |
137 | 0 | struct stat st; |
138 | |
|
139 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index)) |
140 | 0 | return FALSE; |
141 | | |
142 | 0 | if (stat(index->dir, &st) < 0) { |
143 | 0 | if (errno != ENOENT) |
144 | 0 | e_error(index->event, "stat(%s) failed: %m", index->dir); |
145 | 0 | return FALSE; |
146 | 0 | } |
147 | | |
148 | 0 | index->set.mode = st.st_mode & 0666; |
149 | 0 | if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { |
150 | | /* directory's GID is used automatically for new files */ |
151 | 0 | index->set.gid = (gid_t)-1; |
152 | 0 | } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { |
153 | | /* group has same permissions as world, so don't bother |
154 | | changing it */ |
155 | 0 | index->set.gid = (gid_t)-1; |
156 | 0 | } else if (getegid() == st.st_gid) { |
157 | | /* using our own gid, no need to change it */ |
158 | 0 | index->set.gid = (gid_t)-1; |
159 | 0 | } else { |
160 | 0 | index->set.gid = st.st_gid; |
161 | 0 | } |
162 | |
|
163 | 0 | i_free(index->set.gid_origin); |
164 | 0 | if (index->set.gid != (gid_t)-1) |
165 | 0 | index->set.gid_origin = i_strdup("preserved existing GID"); |
166 | 0 | return TRUE; |
167 | 0 | } |
168 | | |
169 | | void mail_index_set_permissions(struct mail_index *index, |
170 | | mode_t mode, gid_t gid, const char *gid_origin) |
171 | 0 | { |
172 | 0 | index->set.mode = mode & 0666; |
173 | 0 | index->set.gid = gid; |
174 | |
|
175 | 0 | i_free(index->set.gid_origin); |
176 | 0 | index->set.gid_origin = i_strdup(gid_origin); |
177 | 0 | } |
178 | | |
179 | | void mail_index_set_lock_method(struct mail_index *index, |
180 | | enum file_lock_method lock_method, |
181 | | unsigned int max_timeout_secs) |
182 | 0 | { |
183 | 0 | index->set.lock_method = lock_method; |
184 | 0 | index->set.max_lock_timeout_secs = max_timeout_secs; |
185 | 0 | } |
186 | | |
187 | | void mail_index_set_optimization_settings(struct mail_index *index, |
188 | | const struct mail_index_optimization_settings *set) |
189 | 0 | { |
190 | 0 | struct mail_index_optimization_settings *dest = |
191 | 0 | &index->optimization_set; |
192 | | |
193 | | /* index */ |
194 | 0 | if (set->index.rewrite_min_log_bytes != 0) |
195 | 0 | dest->index.rewrite_min_log_bytes = set->index.rewrite_min_log_bytes; |
196 | 0 | if (set->index.rewrite_max_log_bytes != 0) |
197 | 0 | dest->index.rewrite_max_log_bytes = set->index.rewrite_max_log_bytes; |
198 | | |
199 | | /* log */ |
200 | 0 | if (set->log.min_size != 0) |
201 | 0 | dest->log.min_size = set->log.min_size; |
202 | 0 | if (set->log.max_size != 0) |
203 | 0 | dest->log.max_size = set->log.max_size; |
204 | 0 | if (set->log.min_age_secs != 0) |
205 | 0 | dest->log.min_age_secs = set->log.min_age_secs; |
206 | 0 | if (set->log.log2_max_age_secs != 0) |
207 | 0 | dest->log.log2_max_age_secs = set->log.log2_max_age_secs; |
208 | | |
209 | | /* cache */ |
210 | 0 | if (set->cache.unaccessed_field_drop_secs != 0) |
211 | 0 | dest->cache.unaccessed_field_drop_secs = |
212 | 0 | set->cache.unaccessed_field_drop_secs; |
213 | 0 | if (set->cache.max_size != 0) |
214 | 0 | dest->cache.max_size = set->cache.max_size; |
215 | 0 | if (set->cache.purge_min_size != 0) |
216 | 0 | dest->cache.purge_min_size = set->cache.purge_min_size; |
217 | 0 | if (set->cache.purge_delete_percentage != 0) |
218 | 0 | dest->cache.purge_delete_percentage = |
219 | 0 | set->cache.purge_delete_percentage; |
220 | 0 | if (set->cache.purge_continued_percentage != 0) |
221 | 0 | dest->cache.purge_continued_percentage = |
222 | 0 | set->cache.purge_continued_percentage; |
223 | 0 | if (set->cache.purge_header_continue_count != 0) |
224 | 0 | dest->cache.purge_header_continue_count = |
225 | 0 | set->cache.purge_header_continue_count; |
226 | 0 | if (set->cache.record_max_size != 0) |
227 | 0 | dest->cache.record_max_size = set->cache.record_max_size; |
228 | |
|
229 | 0 | dest->cache.max_header_name_length = set->cache.max_header_name_length; |
230 | 0 | dest->cache.max_headers_count = set->cache.max_headers_count; |
231 | 0 | } |
232 | | |
233 | | void mail_index_set_ext_init_data(struct mail_index *index, uint32_t ext_id, |
234 | | const void *data, size_t size) |
235 | 0 | { |
236 | 0 | const struct mail_index_registered_ext *rext; |
237 | |
|
238 | 0 | i_assert(index->set.ext_hdr_init_data == NULL || |
239 | 0 | index->set.ext_hdr_init_id == ext_id); |
240 | 0 | i_assert(size > 0); |
241 | | |
242 | 0 | rext = array_idx(&index->extensions, ext_id); |
243 | 0 | i_assert(rext->hdr_size == size); |
244 | | |
245 | 0 | index->set.ext_hdr_init_id = ext_id; |
246 | 0 | i_free(index->set.ext_hdr_init_data); |
247 | 0 | index->set.ext_hdr_init_data = i_malloc(size); |
248 | 0 | memcpy(index->set.ext_hdr_init_data, data, size); |
249 | 0 | } |
250 | | |
251 | | bool mail_index_ext_name_is_valid(const char *name) |
252 | 0 | { |
253 | 0 | size_t i; |
254 | |
|
255 | 0 | for (i = 0; name[i] != '\0'; i++) { |
256 | 0 | if (!i_isalnum(name[i]) && name[i] != '-' && name[i] != '_' && |
257 | 0 | name[i] != ' ') |
258 | 0 | return FALSE; |
259 | |
|
260 | 0 | } |
261 | 0 | return i == 0 || i < MAIL_INDEX_EXT_NAME_MAX_LENGTH; |
262 | 0 | } |
263 | | |
264 | | uint32_t mail_index_ext_register(struct mail_index *index, const char *name, |
265 | | uint32_t default_hdr_size, |
266 | | uint16_t default_record_size, |
267 | | uint16_t default_record_align) |
268 | 0 | { |
269 | 0 | struct mail_index_registered_ext rext; |
270 | 0 | uint32_t ext_id; |
271 | |
|
272 | 0 | if (!mail_index_ext_name_is_valid(name)) |
273 | 0 | i_panic("mail_index_ext_register(%s): Invalid name", name); |
274 | | |
275 | 0 | if (default_record_size != 0 && default_record_align == 0) { |
276 | 0 | i_panic("mail_index_ext_register(%s): " |
277 | 0 | "Invalid record alignment", name); |
278 | 0 | } |
279 | | |
280 | 0 | if (mail_index_ext_lookup(index, name, &ext_id)) |
281 | 0 | return ext_id; |
282 | | |
283 | 0 | i_zero(&rext); |
284 | 0 | rext.name = p_strdup(index->extension_pool, name); |
285 | 0 | rext.index_idx = array_count(&index->extensions); |
286 | 0 | rext.hdr_size = default_hdr_size; |
287 | 0 | rext.record_size = default_record_size; |
288 | 0 | rext.record_align = default_record_align; |
289 | |
|
290 | 0 | array_push_back(&index->extensions, &rext); |
291 | 0 | return rext.index_idx; |
292 | 0 | } |
293 | | |
294 | | void mail_index_ext_register_resize_defaults(struct mail_index *index, |
295 | | uint32_t ext_id, |
296 | | uint32_t default_hdr_size, |
297 | | uint16_t default_record_size, |
298 | | uint16_t default_record_align) |
299 | 0 | { |
300 | 0 | struct mail_index_registered_ext *rext; |
301 | |
|
302 | 0 | rext = array_idx_modifiable(&index->extensions, ext_id); |
303 | 0 | rext->hdr_size = default_hdr_size; |
304 | 0 | rext->record_size = default_record_size; |
305 | 0 | rext->record_align = default_record_align; |
306 | 0 | } |
307 | | |
308 | | bool mail_index_ext_lookup(struct mail_index *index, const char *name, |
309 | | uint32_t *ext_id_r) |
310 | 0 | { |
311 | 0 | const struct mail_index_registered_ext *extensions; |
312 | 0 | unsigned int i, count; |
313 | |
|
314 | 0 | extensions = array_get(&index->extensions, &count); |
315 | 0 | for (i = 0; i < count; i++) { |
316 | 0 | if (strcmp(extensions[i].name, name) == 0) { |
317 | 0 | *ext_id_r = i; |
318 | 0 | return TRUE; |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | 0 | *ext_id_r = (uint32_t)-1; |
323 | 0 | return FALSE; |
324 | 0 | } |
325 | | |
326 | | void mail_index_register_expunge_handler(struct mail_index *index, |
327 | | uint32_t ext_id, |
328 | | mail_index_expunge_handler_t *cb) |
329 | 0 | { |
330 | 0 | struct mail_index_registered_ext *rext; |
331 | |
|
332 | 0 | rext = array_idx_modifiable(&index->extensions, ext_id); |
333 | 0 | i_assert(rext->expunge_handler == NULL || rext->expunge_handler == cb); |
334 | | |
335 | 0 | rext->expunge_handler = cb; |
336 | 0 | } |
337 | | |
338 | | void mail_index_unregister_expunge_handler(struct mail_index *index, |
339 | | uint32_t ext_id) |
340 | 0 | { |
341 | 0 | struct mail_index_registered_ext *rext; |
342 | |
|
343 | 0 | rext = array_idx_modifiable(&index->extensions, ext_id); |
344 | 0 | i_assert(rext->expunge_handler != NULL); |
345 | | |
346 | 0 | rext->expunge_handler = NULL; |
347 | 0 | } |
348 | | |
349 | | bool mail_index_keyword_lookup(struct mail_index *index, |
350 | | const char *keyword, unsigned int *idx_r) |
351 | 0 | { |
352 | 0 | char *key; |
353 | 0 | void *value; |
354 | | |
355 | | /* keywords_hash keeps a name => index mapping of keywords. |
356 | | Keywords are never removed from it, so the index values are valid |
357 | | for the lifetime of the mail_index. */ |
358 | 0 | if (hash_table_lookup_full(index->keywords_hash, keyword, |
359 | 0 | &key, &value)) { |
360 | 0 | *idx_r = POINTER_CAST_TO(value, unsigned int); |
361 | 0 | return TRUE; |
362 | 0 | } |
363 | | |
364 | 0 | *idx_r = UINT_MAX; |
365 | 0 | return FALSE; |
366 | 0 | } |
367 | | |
368 | | void mail_index_keyword_lookup_or_create(struct mail_index *index, |
369 | | const char *keyword, |
370 | | unsigned int *idx_r) |
371 | 0 | { |
372 | 0 | char *keyword_dup; |
373 | |
|
374 | 0 | i_assert(*keyword != '\0'); |
375 | | |
376 | 0 | if (mail_index_keyword_lookup(index, keyword, idx_r)) |
377 | 0 | return; |
378 | | |
379 | 0 | keyword = keyword_dup = p_strdup(index->keywords_pool, keyword); |
380 | 0 | *idx_r = array_count(&index->keywords); |
381 | |
|
382 | 0 | hash_table_insert(index->keywords_hash, keyword_dup, |
383 | 0 | POINTER_CAST(*idx_r)); |
384 | 0 | array_push_back(&index->keywords, &keyword); |
385 | | |
386 | | /* keep the array NULL-terminated, but the NULL itself invisible */ |
387 | 0 | array_append_zero(&index->keywords); |
388 | 0 | array_pop_back(&index->keywords); |
389 | 0 | } |
390 | | |
391 | | const ARRAY_TYPE(keywords) *mail_index_get_keywords(struct mail_index *index) |
392 | 0 | { |
393 | 0 | return &index->keywords; |
394 | 0 | } |
395 | | |
396 | | struct mail_keywords * |
397 | | mail_index_keywords_create(struct mail_index *index, |
398 | | const char *const keywords[]) |
399 | 0 | { |
400 | 0 | struct mail_keywords *k; |
401 | 0 | unsigned int src, dest, i, count; |
402 | |
|
403 | 0 | count = str_array_length(keywords); |
404 | 0 | if (count == 0) { |
405 | 0 | k = i_new(struct mail_keywords, 1); |
406 | 0 | k->index = index; |
407 | 0 | k->refcount = 1; |
408 | 0 | return k; |
409 | 0 | } |
410 | | |
411 | | /* @UNSAFE */ |
412 | 0 | k = i_malloc(MALLOC_ADD(sizeof(struct mail_keywords), |
413 | 0 | MALLOC_MULTIPLY(sizeof(k->idx[0]), count))); |
414 | 0 | k->index = index; |
415 | 0 | k->refcount = 1; |
416 | | |
417 | | /* look up the keywords from index. they're never removed from there |
418 | | so we can permanently store indexes to them. */ |
419 | 0 | for (src = dest = 0; src < count; src++) { |
420 | 0 | mail_index_keyword_lookup_or_create(index, keywords[src], |
421 | 0 | &k->idx[dest]); |
422 | | /* ignore if this is a duplicate */ |
423 | 0 | for (i = 0; i < src; i++) { |
424 | 0 | if (k->idx[i] == k->idx[dest]) |
425 | 0 | break; |
426 | 0 | } |
427 | 0 | if (i == src) |
428 | 0 | dest++; |
429 | 0 | } |
430 | 0 | k->count = dest; |
431 | 0 | return k; |
432 | 0 | } |
433 | | |
434 | | struct mail_keywords * |
435 | | mail_index_keywords_create_from_indexes(struct mail_index *index, |
436 | | const ARRAY_TYPE(keyword_indexes) |
437 | | *keyword_indexes) |
438 | 0 | { |
439 | 0 | struct mail_keywords *k; |
440 | 0 | const unsigned int *indexes; |
441 | 0 | unsigned int src, dest, i, count; |
442 | |
|
443 | 0 | indexes = array_get(keyword_indexes, &count); |
444 | 0 | if (count == 0) { |
445 | 0 | k = i_new(struct mail_keywords, 1); |
446 | 0 | k->index = index; |
447 | 0 | k->refcount = 1; |
448 | 0 | return k; |
449 | 0 | } |
450 | | |
451 | | /* @UNSAFE */ |
452 | 0 | k = i_malloc(MALLOC_ADD(sizeof(struct mail_keywords), |
453 | 0 | MALLOC_MULTIPLY(sizeof(k->idx[0]), count))); |
454 | 0 | k->index = index; |
455 | 0 | k->refcount = 1; |
456 | | |
457 | | /* copy but skip duplicates */ |
458 | 0 | for (src = dest = 0; src < count; src++) { |
459 | 0 | for (i = 0; i < src; i++) { |
460 | 0 | if (k->idx[i] == indexes[src]) |
461 | 0 | break; |
462 | 0 | } |
463 | 0 | if (i == src) |
464 | 0 | k->idx[dest++] = indexes[src]; |
465 | 0 | } |
466 | 0 | k->count = dest; |
467 | 0 | return k; |
468 | 0 | } |
469 | | |
470 | | void mail_index_keywords_ref(struct mail_keywords *keywords) |
471 | 0 | { |
472 | 0 | keywords->refcount++; |
473 | 0 | } |
474 | | |
475 | | void mail_index_keywords_unref(struct mail_keywords **_keywords) |
476 | 0 | { |
477 | 0 | struct mail_keywords *keywords = *_keywords; |
478 | |
|
479 | 0 | i_assert(keywords->refcount > 0); |
480 | | |
481 | 0 | *_keywords = NULL; |
482 | 0 | if (--keywords->refcount == 0) |
483 | 0 | i_free(keywords); |
484 | 0 | } |
485 | | |
486 | | int mail_index_try_open_only(struct mail_index *index) |
487 | 0 | { |
488 | 0 | i_assert(index->fd == -1); |
489 | 0 | i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); |
490 | | |
491 | | /* Note that our caller must close index->fd by itself. */ |
492 | 0 | if (index->readonly) |
493 | 0 | errno = EACCES; |
494 | 0 | else { |
495 | 0 | index->fd = nfs_safe_open(index->filepath, O_RDWR); |
496 | 0 | index->readonly = FALSE; |
497 | 0 | } |
498 | |
|
499 | 0 | if (index->fd == -1 && ENOACCESS(errno)) { |
500 | 0 | index->fd = open(index->filepath, O_RDONLY); |
501 | 0 | index->readonly = TRUE; |
502 | 0 | } |
503 | |
|
504 | 0 | if (index->fd == -1) { |
505 | 0 | if (errno != ENOENT) { |
506 | 0 | mail_index_set_syscall_error(index, "open()"); |
507 | 0 | return -1; |
508 | 0 | } |
509 | | |
510 | | /* have to create it */ |
511 | 0 | return 0; |
512 | 0 | } |
513 | 0 | return 1; |
514 | 0 | } |
515 | | |
516 | | static int |
517 | | mail_index_try_open(struct mail_index *index) |
518 | 0 | { |
519 | 0 | int ret; |
520 | |
|
521 | 0 | i_assert(index->fd == -1); |
522 | | |
523 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index)) |
524 | 0 | return 0; |
525 | | |
526 | 0 | ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD); |
527 | 0 | if (ret == 0 && !index->readonly) { |
528 | | /* it's corrupted - recreate it */ |
529 | 0 | if (index->fd != -1) { |
530 | 0 | if (close(index->fd) < 0) |
531 | 0 | mail_index_set_syscall_error(index, "close()"); |
532 | 0 | index->fd = -1; |
533 | 0 | } |
534 | 0 | } |
535 | 0 | return ret; |
536 | 0 | } |
537 | | |
538 | | int mail_index_create_tmp_file(struct mail_index *index, |
539 | | const char *path_prefix, const char **path_r) |
540 | 0 | { |
541 | 0 | mode_t old_mask; |
542 | 0 | const char *path; |
543 | 0 | int fd; |
544 | |
|
545 | 0 | i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); |
546 | | |
547 | 0 | path = *path_r = t_strconcat(path_prefix, ".tmp", NULL); |
548 | 0 | old_mask = umask(0); |
549 | 0 | fd = open(path, O_RDWR|O_CREAT | O_EXCL | O_NOFOLLOW, index->set.mode); |
550 | 0 | umask(old_mask); |
551 | 0 | if (fd == -1 && errno == EEXIST) { |
552 | | /* stale temp file. unlink and recreate rather than overwriting, |
553 | | just to make sure locking problems won't cause corruption */ |
554 | 0 | if (i_unlink(path) < 0) |
555 | 0 | return -1; |
556 | 0 | old_mask = umask(0); |
557 | 0 | fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, |
558 | 0 | index->set.mode); |
559 | 0 | umask(old_mask); |
560 | 0 | } |
561 | 0 | if (fd == -1) { |
562 | 0 | mail_index_file_set_syscall_error(index, path, "creat()"); |
563 | 0 | return -1; |
564 | 0 | } |
565 | | |
566 | 0 | mail_index_fchown(index, fd, path); |
567 | 0 | return fd; |
568 | 0 | } |
569 | | |
570 | | static const char *mail_index_get_cache_path(struct mail_index *index) |
571 | 0 | { |
572 | 0 | const char *dir; |
573 | |
|
574 | 0 | if (index->set.cache_dir != NULL) |
575 | 0 | dir = index->set.cache_dir; |
576 | 0 | else if (index->dir != NULL) |
577 | 0 | dir = index->dir; |
578 | 0 | else |
579 | 0 | return NULL; |
580 | 0 | return t_strconcat(dir, "/", index->prefix, |
581 | 0 | MAIL_CACHE_FILE_SUFFIX, NULL); |
582 | 0 | } |
583 | | |
584 | | static int mail_index_open_files(struct mail_index *index, |
585 | | enum mail_index_open_flags flags) |
586 | 0 | { |
587 | 0 | int ret; |
588 | |
|
589 | 0 | ret = mail_transaction_log_open(index->log); |
590 | 0 | if (ret == 0) { |
591 | 0 | if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) |
592 | 0 | return 0; |
593 | | |
594 | | /* if dovecot.index exists, read it first so that we can get |
595 | | the correct indexid and log sequence */ |
596 | 0 | (void)mail_index_try_open(index); |
597 | |
|
598 | 0 | if (index->indexid == 0) { |
599 | | /* Create a new indexid for us. If we're opening index |
600 | | into memory, index->map doesn't exist yet. */ |
601 | 0 | index->indexid = ioloop_time32; |
602 | 0 | index->initial_create = TRUE; |
603 | 0 | if (index->map != NULL) |
604 | 0 | index->map->hdr.indexid = index->indexid; |
605 | 0 | } |
606 | |
|
607 | 0 | ret = mail_transaction_log_create(index->log, FALSE); |
608 | 0 | if (index->map != NULL) { |
609 | | /* log creation could have changed it if someone else |
610 | | just created it. */ |
611 | 0 | index->map->hdr.indexid = index->indexid; |
612 | 0 | } |
613 | 0 | index->initial_create = FALSE; |
614 | 0 | index->initial_created = TRUE; |
615 | 0 | } |
616 | 0 | if (ret >= 0) { |
617 | 0 | ret = index->map != NULL ? 1 : mail_index_try_open(index); |
618 | 0 | if (ret == 0 && !index->readonly) { |
619 | | /* corrupted */ |
620 | 0 | mail_transaction_log_close(index->log); |
621 | 0 | ret = mail_transaction_log_create(index->log, TRUE); |
622 | 0 | if (ret == 0) { |
623 | 0 | if (index->map != NULL) |
624 | 0 | mail_index_unmap(&index->map); |
625 | 0 | index->map = mail_index_map_alloc(index); |
626 | 0 | } |
627 | 0 | } |
628 | 0 | } |
629 | 0 | if (ret < 0) { |
630 | | /* open/create failed, fallback to in-memory indexes */ |
631 | 0 | if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) |
632 | 0 | return -1; |
633 | | |
634 | 0 | if (mail_index_move_to_memory(index) < 0) |
635 | 0 | return -1; |
636 | 0 | } |
637 | | |
638 | 0 | if (index->cache == NULL) { |
639 | 0 | const char *path = mail_index_get_cache_path(index); |
640 | 0 | index->cache = mail_cache_open_or_create_path(index, path); |
641 | 0 | } |
642 | 0 | return 1; |
643 | 0 | } |
644 | | |
645 | | static int |
646 | | mail_index_open_opened(struct mail_index *index, |
647 | | enum mail_index_open_flags flags) |
648 | 0 | { |
649 | 0 | int ret; |
650 | |
|
651 | 0 | i_assert(index->map != NULL); |
652 | | |
653 | 0 | if ((index->map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) { |
654 | | /* index was marked corrupted. we'll probably need to |
655 | | recreate the files. */ |
656 | 0 | mail_index_unmap(&index->map); |
657 | 0 | mail_index_close_file(index); |
658 | 0 | mail_transaction_log_close(index->log); |
659 | 0 | if ((ret = mail_index_open_files(index, flags)) <= 0) |
660 | 0 | return ret; |
661 | 0 | } |
662 | | |
663 | 0 | index->open_count++; |
664 | 0 | return 1; |
665 | 0 | } |
666 | | |
667 | | int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags) |
668 | 0 | { |
669 | 0 | int ret; |
670 | |
|
671 | 0 | if (index->open_count > 0) { |
672 | 0 | if ((ret = mail_index_open_opened(index, flags)) <= 0) { |
673 | | /* doesn't exist and create flag not used */ |
674 | 0 | } |
675 | 0 | return ret; |
676 | 0 | } |
677 | | |
678 | 0 | index->filepath = MAIL_INDEX_IS_IN_MEMORY(index) ? |
679 | 0 | i_strdup("(in-memory index)") : |
680 | 0 | i_strconcat(index->dir, "/", index->prefix, NULL); |
681 | |
|
682 | 0 | mail_index_reset_error(index); |
683 | 0 | index->readonly = FALSE; |
684 | 0 | index->log_sync_locked = FALSE; |
685 | 0 | index->flags = flags; |
686 | 0 | index->readonly = (flags & MAIL_INDEX_OPEN_FLAG_READONLY) != 0; |
687 | 0 | if ((flags & MAIL_INDEX_OPEN_FLAG_DEBUG) != 0) |
688 | 0 | event_set_forced_debug(index->event, TRUE); |
689 | 0 | else |
690 | 0 | event_unset_forced_debug(index->event); |
691 | |
|
692 | 0 | if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 && |
693 | 0 | index->set.fsync_mode != FSYNC_MODE_ALWAYS) |
694 | 0 | i_fatal("nfs flush requires mail_fsync=always"); |
695 | 0 | if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 && |
696 | 0 | (flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0) |
697 | 0 | i_fatal("nfs flush requires mmap_disable=yes"); |
698 | | |
699 | | /* NOTE: increase open_count only after mail_index_open_files(). |
700 | | it's used elsewhere to check if we're doing an initial opening |
701 | | of the index files */ |
702 | 0 | if ((ret = mail_index_open_files(index, flags)) <= 0) { |
703 | | /* doesn't exist and create flag not used */ |
704 | 0 | mail_index_close_nonopened(index); |
705 | 0 | return ret; |
706 | 0 | } |
707 | | |
708 | 0 | index->open_count++; |
709 | |
|
710 | 0 | if (index->log->head == NULL) { |
711 | 0 | mail_index_close(index); |
712 | 0 | mail_index_set_error(index, "Index is corrupted " |
713 | 0 | "(log->view->head == NULL)"); |
714 | 0 | return -1; |
715 | 0 | } |
716 | | |
717 | 0 | i_assert(index->map != NULL); |
718 | 0 | mail_index_alloc_cache_index_opened(index); |
719 | 0 | return 1; |
720 | 0 | } |
721 | | |
722 | | int mail_index_open_or_create(struct mail_index *index, |
723 | | enum mail_index_open_flags flags) |
724 | 0 | { |
725 | 0 | int ret; |
726 | |
|
727 | 0 | flags |= MAIL_INDEX_OPEN_FLAG_CREATE; |
728 | 0 | ret = mail_index_open(index, flags); |
729 | 0 | i_assert(ret != 0); |
730 | 0 | return ret < 0 ? -1 : (index->initial_created ? 1 : 0); |
731 | 0 | } |
732 | | |
733 | | void mail_index_close_file(struct mail_index *index) |
734 | 0 | { |
735 | 0 | if (index->fd != -1) { |
736 | 0 | if (close(index->fd) < 0) |
737 | 0 | mail_index_set_syscall_error(index, "close()"); |
738 | 0 | index->fd = -1; |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | | static void mail_index_close_nonopened(struct mail_index *index) |
743 | 0 | { |
744 | 0 | i_assert(!index->syncing); |
745 | | |
746 | 0 | if (index->views != NULL) { |
747 | 0 | i_panic("Leaked view for index %s: Opened in %s:%u", |
748 | 0 | index->filepath, index->views->source_filename, |
749 | 0 | index->views->source_linenum); |
750 | 0 | } |
751 | 0 | i_assert(index->views == NULL); |
752 | | |
753 | 0 | mail_transaction_log_finish_pending_rotation(index->log); |
754 | |
|
755 | 0 | if (index->map != NULL) |
756 | 0 | mail_index_unmap(&index->map); |
757 | |
|
758 | 0 | mail_index_close_file(index); |
759 | 0 | mail_transaction_log_close(index->log); |
760 | 0 | if (index->cache != NULL) |
761 | 0 | mail_cache_free(&index->cache); |
762 | |
|
763 | 0 | i_free_and_null(index->filepath); |
764 | |
|
765 | 0 | index->indexid = 0; |
766 | 0 | } |
767 | | |
768 | | void mail_index_close(struct mail_index *index) |
769 | 0 | { |
770 | 0 | i_assert(index->open_count > 0); |
771 | | |
772 | 0 | mail_index_alloc_cache_index_closing(index); |
773 | 0 | if (--index->open_count == 0) |
774 | 0 | mail_index_close_nonopened(index); |
775 | 0 | } |
776 | | |
777 | | int mail_index_unlink(struct mail_index *index) |
778 | 0 | { |
779 | 0 | const char *path; |
780 | 0 | int last_errno = 0; |
781 | |
|
782 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index) || index->readonly) |
783 | 0 | return 0; |
784 | | |
785 | | /* main index */ |
786 | 0 | if (unlink(index->filepath) < 0 && errno != ENOENT) |
787 | 0 | last_errno = errno; |
788 | | |
789 | | /* logs */ |
790 | 0 | path = t_strconcat(index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL); |
791 | 0 | if (unlink(path) < 0 && errno != ENOENT) |
792 | 0 | last_errno = errno; |
793 | |
|
794 | 0 | path = t_strconcat(index->filepath, |
795 | 0 | MAIL_TRANSACTION_LOG_SUFFIX".2", NULL); |
796 | 0 | if (unlink(path) < 0 && errno != ENOENT) |
797 | 0 | last_errno = errno; |
798 | | |
799 | | /* cache */ |
800 | 0 | path = t_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL); |
801 | 0 | if (unlink(path) < 0 && errno != ENOENT) |
802 | 0 | last_errno = errno; |
803 | |
|
804 | 0 | if (last_errno == 0) |
805 | 0 | return 0; |
806 | 0 | else { |
807 | 0 | errno = last_errno; |
808 | 0 | return -1; |
809 | 0 | } |
810 | 0 | } |
811 | | |
812 | | int mail_index_reopen_if_changed(struct mail_index *index, bool *reopened_r, |
813 | | const char **reason_r) |
814 | 0 | { |
815 | 0 | struct stat st1, st2; |
816 | 0 | int ret; |
817 | |
|
818 | 0 | *reopened_r = FALSE; |
819 | |
|
820 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index)) { |
821 | 0 | *reason_r = "in-memory index"; |
822 | 0 | return 0; |
823 | 0 | } |
824 | | |
825 | 0 | if (index->fd == -1) |
826 | 0 | goto final; |
827 | | |
828 | 0 | if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) |
829 | 0 | nfs_flush_file_handle_cache(index->filepath); |
830 | 0 | if (nfs_safe_stat(index->filepath, &st2) < 0) { |
831 | 0 | if (errno == ENOENT) { |
832 | 0 | *reason_r = "index not found via stat()"; |
833 | 0 | return 0; |
834 | 0 | } |
835 | 0 | mail_index_set_syscall_error(index, "stat()"); |
836 | 0 | return -1; |
837 | 0 | } |
838 | | |
839 | 0 | if (fstat(index->fd, &st1) < 0) { |
840 | 0 | if (!ESTALE_FSTAT(errno)) { |
841 | 0 | mail_index_set_syscall_error(index, "fstat()"); |
842 | 0 | return -1; |
843 | 0 | } |
844 | | /* deleted/recreated, reopen */ |
845 | 0 | *reason_r = "index is stale"; |
846 | 0 | } else if (st1.st_ino == st2.st_ino && |
847 | 0 | CMP_DEV_T(st1.st_dev, st2.st_dev)) { |
848 | | /* the same file */ |
849 | 0 | *reason_r = "index unchanged"; |
850 | 0 | return 1; |
851 | 0 | } else { |
852 | 0 | *reason_r = "index inode changed"; |
853 | 0 | } |
854 | | |
855 | | /* new file, new locks. the old fd can keep its locks, they don't |
856 | | matter anymore as no-one's going to modify the file. */ |
857 | 0 | mail_index_close_file(index); |
858 | |
|
859 | 0 | final: |
860 | 0 | if ((ret = mail_index_try_open_only(index)) == 0) |
861 | 0 | *reason_r = "index not found via open()"; |
862 | 0 | else if (ret > 0) { |
863 | 0 | *reason_r = "index opened"; |
864 | 0 | *reopened_r = TRUE; |
865 | 0 | } |
866 | 0 | return ret; |
867 | 0 | } |
868 | | |
869 | | int mail_index_refresh(struct mail_index *index) |
870 | 0 | { |
871 | 0 | int ret; |
872 | |
|
873 | 0 | ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD); |
874 | 0 | return ret <= 0 ? -1 : 0; |
875 | 0 | } |
876 | | |
877 | | struct mail_cache *mail_index_get_cache(struct mail_index *index) |
878 | 0 | { |
879 | 0 | return index->cache; |
880 | 0 | } |
881 | | |
882 | | static void ATTR_FORMAT(2,0) |
883 | | mail_index_set_verror(struct mail_index *index, const char *fmt, va_list args) |
884 | 0 | { |
885 | 0 | i_free(index->last_error.text); |
886 | |
|
887 | 0 | if (fmt == NULL) |
888 | 0 | index->last_error.text = NULL; |
889 | 0 | else { |
890 | 0 | index->last_error.text = i_strdup_vprintf(fmt, args); |
891 | 0 | e_error(index->event, "%s", index->last_error.text); |
892 | 0 | } |
893 | 0 | } |
894 | | |
895 | | void mail_index_set_error(struct mail_index *index, const char *fmt, ...) |
896 | 0 | { |
897 | 0 | va_list args; |
898 | 0 | va_start(args, fmt); |
899 | 0 | mail_index_set_verror(index, fmt, args); |
900 | 0 | va_end(args); |
901 | 0 | } |
902 | | |
903 | | void mail_index_set_error_code(struct mail_index *index, |
904 | | enum mail_index_error_code code, |
905 | | const char *fmt, ...) |
906 | 0 | { |
907 | 0 | va_list args; |
908 | 0 | va_start(args, fmt); |
909 | 0 | mail_index_set_verror(index, fmt, args); |
910 | 0 | index->last_error.code = code; |
911 | 0 | va_end(args); |
912 | 0 | } |
913 | | |
914 | | void mail_index_set_error_nolog(struct mail_index *index, const char *str) |
915 | 0 | { |
916 | 0 | i_assert(str != NULL); |
917 | | |
918 | 0 | char *old_error = index->last_error.text; |
919 | 0 | index->last_error.text = i_strdup(str); |
920 | 0 | i_free(old_error); |
921 | 0 | } |
922 | | |
923 | | bool mail_index_is_in_memory(struct mail_index *index) |
924 | 0 | { |
925 | 0 | return MAIL_INDEX_IS_IN_MEMORY(index); |
926 | 0 | } |
927 | | |
928 | | static void mail_index_set_as_in_memory(struct mail_index *index) |
929 | 0 | { |
930 | 0 | i_free_and_null(index->dir); |
931 | |
|
932 | 0 | i_free(index->filepath); |
933 | 0 | index->filepath = i_strdup("(in-memory index)"); |
934 | 0 | } |
935 | | |
936 | | int mail_index_move_to_memory(struct mail_index *index) |
937 | 0 | { |
938 | 0 | struct mail_index_map *map; |
939 | |
|
940 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index)) |
941 | 0 | return index->map == NULL ? -1 : 0; |
942 | | |
943 | 0 | if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) |
944 | 0 | return -1; |
945 | | |
946 | 0 | if (index->map == NULL) { |
947 | | /* index was never even opened. just mark it as being in |
948 | | memory and let the caller re-open the index. */ |
949 | 0 | i_assert(index->fd == -1); |
950 | 0 | mail_index_set_as_in_memory(index); |
951 | 0 | return -1; |
952 | 0 | } |
953 | | |
954 | | /* move index map to memory */ |
955 | 0 | if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) { |
956 | 0 | map = mail_index_map_clone(index->map); |
957 | 0 | mail_index_unmap(&index->map); |
958 | 0 | index->map = map; |
959 | 0 | } |
960 | |
|
961 | 0 | if (index->log != NULL) { |
962 | | /* move transaction log to memory */ |
963 | 0 | if (mail_transaction_log_move_to_memory(index->log) < 0) |
964 | 0 | return -1; |
965 | 0 | } |
966 | | |
967 | 0 | if (index->fd != -1) { |
968 | 0 | if (close(index->fd) < 0) |
969 | 0 | mail_index_set_syscall_error(index, "close()"); |
970 | 0 | index->fd = -1; |
971 | 0 | } |
972 | 0 | mail_index_set_as_in_memory(index); |
973 | 0 | return 0; |
974 | 0 | } |
975 | | |
976 | | void mail_index_mark_corrupted(struct mail_index *index) |
977 | 0 | { |
978 | 0 | index->indexid = 0; |
979 | |
|
980 | 0 | index->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED; |
981 | 0 | if (!index->readonly) { |
982 | 0 | if (unlink(index->filepath) < 0 && |
983 | 0 | errno != ENOENT && errno != ESTALE) |
984 | 0 | mail_index_set_syscall_error(index, "unlink()"); |
985 | 0 | (void)mail_transaction_log_unlink(index->log); |
986 | 0 | } |
987 | 0 | } |
988 | | |
989 | | bool mail_index_is_deleted(struct mail_index *index) |
990 | 0 | { |
991 | 0 | return index->index_delete_requested || index->index_deleted; |
992 | 0 | } |
993 | | |
994 | | int mail_index_get_modification_time(struct mail_index *index, time_t *mtime_r) |
995 | 0 | { |
996 | 0 | struct stat st; |
997 | 0 | const char *path; |
998 | |
|
999 | 0 | *mtime_r = 0; |
1000 | 0 | if (MAIL_INDEX_IS_IN_MEMORY(index)) { |
1001 | | /* this function doesn't make sense for in-memory indexes */ |
1002 | 0 | return 0; |
1003 | 0 | } |
1004 | | |
1005 | | /* index may not be open, so index->filepath may be NULL */ |
1006 | 0 | path = t_strconcat(index->dir, "/", index->prefix, |
1007 | 0 | MAIL_TRANSACTION_LOG_SUFFIX, NULL); |
1008 | 0 | if (stat(path, &st) < 0) { |
1009 | 0 | if (errno == ENOENT) { |
1010 | | /* .log is always supposed to exist - don't bother |
1011 | | trying to stat(dovecot.index) */ |
1012 | 0 | return 0; |
1013 | 0 | } |
1014 | 0 | mail_index_file_set_syscall_error(index, path, "stat()"); |
1015 | 0 | return -1; |
1016 | 0 | } |
1017 | 0 | *mtime_r = st.st_mtime; |
1018 | 0 | return 0; |
1019 | 0 | } |
1020 | | |
1021 | | void mail_index_fchown(struct mail_index *index, int fd, const char *path) |
1022 | 0 | { |
1023 | 0 | mode_t mode; |
1024 | |
|
1025 | 0 | if (index->set.gid == (gid_t)-1) { |
1026 | | /* no gid changing */ |
1027 | 0 | return; |
1028 | 0 | } else if (fchown(fd, (uid_t)-1, index->set.gid) == 0) { |
1029 | | /* success */ |
1030 | 0 | return; |
1031 | 0 | } if ((index->set.mode & 0060) >> 3 == (index->set.mode & 0006)) { |
1032 | | /* group and world permissions are the same, so group doesn't |
1033 | | really matter. ignore silently. */ |
1034 | 0 | return; |
1035 | 0 | } |
1036 | 0 | if (!ENOACCESS(errno)) |
1037 | 0 | mail_index_file_set_syscall_error(index, path, "fchown()"); |
1038 | 0 | else { |
1039 | 0 | mail_index_set_error(index, "%s", |
1040 | 0 | eperm_error_get_chgrp("fchown", path, index->set.gid, |
1041 | 0 | index->set.gid_origin)); |
1042 | 0 | } |
1043 | | |
1044 | | /* continue, but change permissions so that only the common |
1045 | | subset of group and world is used. this makes sure no one |
1046 | | gets any extra permissions. */ |
1047 | 0 | mode = ((index->set.mode & 0060) >> 3) & (index->set.mode & 0006); |
1048 | 0 | mode |= (mode << 3) | (index->set.mode & 0600); |
1049 | 0 | if (fchmod(fd, mode) < 0) |
1050 | 0 | mail_index_file_set_syscall_error(index, path, "fchmod()"); |
1051 | 0 | } |
1052 | | |
1053 | | int mail_index_lock_sync(struct mail_index *index, const char *lock_reason) |
1054 | 0 | { |
1055 | 0 | uint32_t file_seq; |
1056 | 0 | uoff_t file_offset; |
1057 | |
|
1058 | 0 | return mail_transaction_log_sync_lock(index->log, lock_reason, |
1059 | 0 | &file_seq, &file_offset); |
1060 | 0 | } |
1061 | | |
1062 | | void mail_index_unlock(struct mail_index *index, const char *long_lock_reason) |
1063 | 0 | { |
1064 | 0 | mail_transaction_log_sync_unlock(index->log, long_lock_reason); |
1065 | 0 | } |
1066 | | |
1067 | | bool mail_index_is_locked(struct mail_index *index) |
1068 | 0 | { |
1069 | 0 | return index->log_sync_locked; |
1070 | 0 | } |
1071 | | |
1072 | | void mail_index_set_syscall_error(struct mail_index *index, |
1073 | | const char *function) |
1074 | 0 | { |
1075 | 0 | mail_index_file_set_syscall_error(index, index->filepath, function); |
1076 | 0 | } |
1077 | | |
1078 | | void mail_index_file_set_syscall_error(struct mail_index *index, |
1079 | | const char *filepath, |
1080 | | const char *function) |
1081 | 0 | { |
1082 | 0 | const char *errstr; |
1083 | |
|
1084 | 0 | i_assert(filepath != NULL); |
1085 | 0 | i_assert(function != NULL); |
1086 | | |
1087 | 0 | if (errno == ENOENT) { |
1088 | 0 | struct stat st; |
1089 | 0 | int old_errno = errno; |
1090 | 0 | i_assert(index->log->filepath != NULL); |
1091 | 0 | if (nfs_safe_stat(index->log->filepath, &st) < 0 && |
1092 | 0 | errno == ENOENT) { |
1093 | | /* the index log has gone away */ |
1094 | 0 | index->index_deleted = TRUE; |
1095 | 0 | errno = old_errno; |
1096 | 0 | return; |
1097 | 0 | } |
1098 | 0 | errno = old_errno; |
1099 | 0 | } |
1100 | | |
1101 | 0 | if (ENOSPACE(errno)) { |
1102 | 0 | index->last_error.code = MAIL_INDEX_ERROR_CODE_NO_SPACE; |
1103 | 0 | if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) == 0) |
1104 | 0 | return; |
1105 | 0 | } |
1106 | | |
1107 | 0 | if (ENOACCESS(errno)) { |
1108 | 0 | function = t_strcut(function, '('); |
1109 | 0 | if (strcmp(function, "creat") == 0 || |
1110 | 0 | str_begins_with(function, "file_dotlock_")) |
1111 | 0 | errstr = eacces_error_get_creating(function, filepath); |
1112 | 0 | else |
1113 | 0 | errstr = eacces_error_get(function, filepath); |
1114 | |
|
1115 | 0 | mail_index_set_error_code(index, MAIL_INDEX_ERROR_CODE_NO_ACCESS, |
1116 | 0 | "%s", errstr); |
1117 | 0 | } else { |
1118 | 0 | const char *suffix = errno != EFBIG ? "" : |
1119 | 0 | " (process was started with ulimit -f limit)"; |
1120 | 0 | mail_index_set_error(index, "%s failed with file %s: " |
1121 | 0 | "%m%s", function, filepath, suffix); |
1122 | 0 | } |
1123 | 0 | } |
1124 | | |
1125 | | const char *mail_index_get_last_error(struct mail_index *index, |
1126 | | enum mail_index_error_code *code_r) |
1127 | 0 | { |
1128 | 0 | if (code_r != NULL) |
1129 | 0 | *code_r = index->last_error.code; |
1130 | 0 | return index->last_error.text; |
1131 | 0 | } |
1132 | | |
1133 | | void mail_index_reset_error(struct mail_index *index) |
1134 | 0 | { |
1135 | 0 | i_free(index->last_error.text); |
1136 | 0 | i_zero(&index->last_error); |
1137 | 0 | } |