1 /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "hex-binary.h"
6 #include "mkdir-parents.h"
7 #include "istream.h"
8 #include "ostream.h"
9 #include "time-util.h"
10 #include "home-expand.h"
11 #include "file-create-locked.h"
12 #include "file-dotlock.h"
13 #include "md5.h"
14 #include "hash.h"
15 #include "mail-user.h"
16 #include "mail-storage-settings.h"
17 #include "mail-duplicate.h"
18
19 #include <fcntl.h>
20 #include <unistd.h>
21
22 #define COMPRESS_PERCENTAGE 10
23 #define DUPLICATE_BUFSIZE 4096
24 #define DUPLICATE_VERSION 2
25
26 #define DUPLICATE_LOCK_FNAME_PREFIX "duplicate.lock."
27
28 #define DUPLICATE_LOCK_TIMEOUT_SECS 65
29 #define DUPLICATE_LOCK_WARN_SECS 4
30 #define DUPLICATE_LOCK_MAX_LOCKS 100
31
32 enum mail_duplicate_lock_result {
33 MAIL_DUPLICATE_LOCK_OK,
34 MAIL_DUPLICATE_LOCK_IO_ERROR,
35 MAIL_DUPLICATE_LOCK_TIMEOUT,
36 MAIL_DUPLICATE_LOCK_TOO_MANY,
37 MAIL_DUPLICATE_LOCK_DEADLOCK,
38 };
39
40 struct mail_duplicate_lock {
41 int fd;
42 char *path;
43 struct file_lock *lock;
44 struct timeval start_time;
45 };
46
47 struct mail_duplicate {
48 const void *id;
49 unsigned int id_size;
50
51 const char *user;
52 time_t time;
53 struct mail_duplicate_lock lock;
54
55 bool marked:1;
56 bool changed:1;
57 };
58
59 struct mail_duplicate_file_header {
60 uint32_t version;
61 };
62
63 struct mail_duplicate_record_header {
64 uint32_t stamp;
65 uint32_t id_size;
66 uint32_t user_size;
67 };
68
69 struct mail_duplicate_transaction {
70 pool_t pool;
71 struct mail_duplicate_db *db;
72 ino_t db_ino;
73 struct event *event;
74
75 HASH_TABLE(struct mail_duplicate *, struct mail_duplicate *) hash;
76 const char *path;
77 unsigned int id_lock_count;
78
79 bool changed:1;
80 };
81
82 struct mail_duplicate_db {
83 struct mail_user *user;
84 struct event *event;
85 char *path;
86 char *lock_dir;
87 struct dotlock_settings dotlock_set;
88
89 unsigned int transaction_count;
90 };
91
92 static const struct dotlock_settings default_mail_duplicate_dotlock_set = {
93 .timeout = 20,
94 .stale_timeout = 10,
95 };
96
97 static int
mail_duplicate_cmp(const struct mail_duplicate * d1,const struct mail_duplicate * d2)98 mail_duplicate_cmp(const struct mail_duplicate *d1,
99 const struct mail_duplicate *d2)
100 {
101 return (d1->id_size == d2->id_size &&
102 memcmp(d1->id, d2->id, d1->id_size) == 0 &&
103 strcasecmp(d1->user, d2->user) == 0) ? 0 : 1;
104 }
105
mail_duplicate_hash(const struct mail_duplicate * d)106 static unsigned int mail_duplicate_hash(const struct mail_duplicate *d)
107 {
108 /* a char* hash function from ASU -- from glib */
109 const unsigned char *s = d->id, *end = s + d->id_size;
110 unsigned int g, h = 0;
111
112 while (s != end) {
113 h = (h << 4) + *s;
114 if ((g = h & 0xf0000000UL) != 0) {
115 h = h ^ (g >> 24);
116 h = h ^ g;
117 }
118 s++;
119 }
120
121 return h ^ strcase_hash(d->user);
122 }
123
124 static enum mail_duplicate_lock_result
duplicate_lock_failed(struct mail_duplicate_transaction * trans,struct mail_duplicate * dup,const char * error)125 duplicate_lock_failed(struct mail_duplicate_transaction *trans,
126 struct mail_duplicate *dup, const char *error)
127 {
128 struct mail_duplicate_lock *lock = &dup->lock;
129 enum mail_duplicate_lock_result result;
130 int diff;
131
132 i_assert(lock->fd == -1);
133 i_assert(lock->lock == NULL);
134
135 if (errno == EDEADLK) {
136 /* deadlock */
137 result = MAIL_DUPLICATE_LOCK_DEADLOCK;
138 } else if (errno != EAGAIN) {
139 /* not a lock timeout */
140 result = MAIL_DUPLICATE_LOCK_IO_ERROR;
141 } else {
142 diff = timeval_diff_msecs(&ioloop_timeval,
143 &lock->start_time);
144 error = t_strdup_printf("Lock timeout in %d.%03d secs",
145 diff/1000, diff%1000);
146 result = MAIL_DUPLICATE_LOCK_TIMEOUT;
147 }
148
149 e_error(trans->event, "Failed to lock %s: %s", lock->path, error);
150 i_free_and_null(lock->path);
151 i_zero(lock);
152 return result;
153 }
154
mail_duplicate_is_locked(struct mail_duplicate * dup)155 static bool mail_duplicate_is_locked(struct mail_duplicate *dup)
156 {
157 struct mail_duplicate_lock *lock = &dup->lock;
158
159 return (lock->lock != NULL);
160 }
161
162 static enum mail_duplicate_lock_result
mail_duplicate_lock(struct mail_duplicate_transaction * trans,struct mail_duplicate * dup)163 mail_duplicate_lock(struct mail_duplicate_transaction *trans,
164 struct mail_duplicate *dup)
165 {
166 struct file_create_settings lock_set = {
167 .lock_timeout_secs = DUPLICATE_LOCK_TIMEOUT_SECS,
168 .lock_settings = {
169 .lock_method = FILE_LOCK_METHOD_FCNTL,
170 .allow_deadlock = TRUE,
171 },
172 };
173 struct mail_duplicate_db *db = trans->db;
174 struct mail_duplicate_lock *lock = &dup->lock;
175 const char *error;
176 unsigned char id_md5[MD5_RESULTLEN];
177 bool created;
178 int diff;
179
180 if (mail_duplicate_is_locked(dup)) {
181 e_debug(trans->event, "Duplicate ID already locked");
182 return MAIL_DUPLICATE_LOCK_OK;
183 }
184 if (trans->id_lock_count >= DUPLICATE_LOCK_MAX_LOCKS) {
185 e_debug(trans->event, "Too many duplicate IDs locked");
186 return MAIL_DUPLICATE_LOCK_TOO_MANY;
187 }
188
189 i_assert(db->lock_dir != NULL);
190
191 lock->start_time = ioloop_timeval;
192 md5_get_digest(dup->id, dup->id_size, id_md5);
193 lock->path = i_strdup_printf("%s/"DUPLICATE_LOCK_FNAME_PREFIX"%s",
194 db->lock_dir,
195 binary_to_hex(id_md5, sizeof(id_md5)));
196
197 e_debug(trans->event, "Lock duplicate ID (path=%s)", lock->path);
198
199 lock->fd = file_create_locked(lock->path, &lock_set, &lock->lock,
200 &created, &error);
201 if (lock->fd == -1 && errno == ENOENT) {
202 /* parent directory missing - create it */
203 if (mkdir_parents(db->lock_dir, 0700) < 0 && errno != EEXIST) {
204 error = t_strdup_printf(
205 "mkdir_parents(%s) failed: %m", db->lock_dir);
206 } else {
207 lock->fd = file_create_locked(lock->path,
208 &lock_set, &lock->lock,
209 &created, &error);
210 }
211 }
212 if (lock->fd == -1)
213 return duplicate_lock_failed(trans, dup, error);
214
215 diff = timeval_diff_msecs(&ioloop_timeval, &lock->start_time);
216 if (diff >= (DUPLICATE_LOCK_WARN_SECS * 1000)) {
217 e_warning(trans->event, "Locking %s took %d.%03d secs",
218 lock->path, diff/1000, diff%1000);
219 }
220
221 i_assert(mail_duplicate_is_locked(dup));
222 trans->id_lock_count++;
223 return MAIL_DUPLICATE_LOCK_OK;
224 }
225
226 static void
mail_duplicate_unlock(struct mail_duplicate_transaction * trans,struct mail_duplicate * dup)227 mail_duplicate_unlock(struct mail_duplicate_transaction *trans,
228 struct mail_duplicate *dup)
229 {
230 int orig_errno = errno;
231
232 if (dup->lock.path != NULL) {
233 struct mail_duplicate_lock *lock = &dup->lock;
234
235 e_debug(trans->event, "Unlock duplicate ID (path=%s)",
236 lock->path);
237 i_unlink(lock->path);
238 file_lock_free(&lock->lock);
239 i_close_fd(&lock->fd);
240 i_free_and_null(lock->path);
241 i_zero(lock);
242
243 i_assert(trans->id_lock_count > 0);
244 trans->id_lock_count--;
245 }
246
247 errno = orig_errno;
248 }
249
250 static int
mail_duplicate_read_records(struct mail_duplicate_transaction * trans,struct istream * input,unsigned int record_size)251 mail_duplicate_read_records(struct mail_duplicate_transaction *trans,
252 struct istream *input,
253 unsigned int record_size)
254 {
255 const unsigned char *data;
256 struct mail_duplicate_record_header hdr;
257 size_t size;
258 unsigned int change_count;
259
260 change_count = 0;
261 while (i_stream_read_bytes(input, &data, &size, record_size) > 0) {
262 if (record_size == sizeof(hdr))
263 memcpy(&hdr, data, sizeof(hdr));
264 else {
265 /* FIXME: backwards compatibility with v1.0 */
266 time_t stamp;
267
268 i_assert(record_size ==
269 sizeof(time_t) + sizeof(uint32_t)*2);
270 memcpy(&stamp, data, sizeof(stamp));
271 hdr.stamp = stamp;
272 memcpy(&hdr.id_size, data + sizeof(time_t),
273 sizeof(hdr.id_size));
274 memcpy(&hdr.user_size,
275 data + sizeof(time_t) + sizeof(uint32_t),
276 sizeof(hdr.user_size));
277 }
278 i_stream_skip(input, record_size);
279
280 if (hdr.id_size == 0 || hdr.user_size == 0 ||
281 hdr.id_size > DUPLICATE_BUFSIZE ||
282 hdr.user_size > DUPLICATE_BUFSIZE) {
283 e_error(trans->event,
284 "broken mail_duplicate file %s", trans->path);
285 return -1;
286 }
287
288 if (i_stream_read_bytes(input, &data, &size,
289 hdr.id_size + hdr.user_size) <= 0) {
290 e_error(trans->event,
291 "unexpected end of file in %s", trans->path);
292 return -1;
293 }
294
295 struct mail_duplicate dup_q, *dup;
296
297 dup_q.id = data;
298 dup_q.id_size = hdr.id_size;
299 dup_q.user = t_strndup(data + hdr.id_size, hdr.user_size);
300
301 dup = hash_table_lookup(trans->hash, &dup_q);
302 if ((time_t)hdr.stamp < ioloop_time) {
303 change_count++;
304 if (dup != NULL && !dup->changed)
305 dup->marked = FALSE;
306 } else {
307 if (dup == NULL) {
308 void *new_id;
309
310 new_id = p_malloc(trans->pool, hdr.id_size);
311 memcpy(new_id, data, hdr.id_size);
312
313 dup = p_new(trans->pool,
314 struct mail_duplicate, 1);
315 dup->id = new_id;
316 dup->id_size = hdr.id_size;
317 dup->user = p_strdup(trans->pool, dup_q.user);
318 hash_table_update(trans->hash, dup, dup);
319 }
320 if (!dup->changed) {
321 dup->marked = TRUE;
322 dup->time = hdr.stamp;
323 }
324 }
325 i_stream_skip(input, hdr.id_size + hdr.user_size);
326 }
327
328 if (hash_table_count(trans->hash) *
329 COMPRESS_PERCENTAGE / 100 > change_count)
330 trans->changed = TRUE;
331 return 0;
332 }
333
334 static int
mail_duplicate_read_db_from_fd(struct mail_duplicate_transaction * trans,int fd)335 mail_duplicate_read_db_from_fd(struct mail_duplicate_transaction *trans, int fd)
336 {
337 struct istream *input;
338 struct mail_duplicate_file_header hdr;
339 const unsigned char *data;
340 size_t size;
341 struct stat st;
342 unsigned int record_size = 0;
343
344 if (fstat(fd, &st) < 0) {
345 e_error(trans->event,
346 "stat(%s) failed: %m", trans->path);
347 return -1;
348 }
349 trans->db_ino = st.st_ino;
350
351 /* <timestamp> <id_size> <user_size> <id> <user> */
352 input = i_stream_create_fd(fd, DUPLICATE_BUFSIZE);
353 if (i_stream_read_bytes(input, &data, &size, sizeof(hdr)) > 0) {
354 memcpy(&hdr, data, sizeof(hdr));
355 if (hdr.version == 0 || hdr.version > DUPLICATE_VERSION + 10) {
356 /* FIXME: backwards compatibility with v1.0 */
357 record_size = sizeof(time_t) + sizeof(uint32_t)*2;
358 } else if (hdr.version == DUPLICATE_VERSION) {
359 record_size = sizeof(struct mail_duplicate_record_header);
360 i_stream_skip(input, sizeof(hdr));
361 }
362 }
363
364 if (record_size == 0)
365 i_unlink_if_exists(trans->path);
366 else T_BEGIN {
367 if (mail_duplicate_read_records(trans, input, record_size) < 0)
368 i_unlink_if_exists(trans->path);
369 } T_END;
370
371 i_stream_unref(&input);
372 return 0;
373 }
374
mail_duplicate_read_db_file(struct mail_duplicate_transaction * trans)375 static int mail_duplicate_read_db_file(struct mail_duplicate_transaction *trans)
376 {
377 int fd, ret;
378
379 e_debug(trans->event, "Reading %s", trans->path);
380
381 fd = open(trans->path, O_RDONLY);
382 if (fd == -1) {
383 if (errno == ENOENT)
384 return 0;
385 e_error(trans->event,
386 "open(%s) failed: %m", trans->path);
387 return -1;
388 }
389
390 ret = mail_duplicate_read_db_from_fd(trans, fd);
391
392 if (close(fd) < 0) {
393 e_error(trans->event,
394 "close(%s) failed: %m", trans->path);
395 }
396 return ret;
397 }
398
mail_duplicate_read(struct mail_duplicate_transaction * trans)399 static void mail_duplicate_read(struct mail_duplicate_transaction *trans)
400 {
401 struct mail_duplicate_db *db = trans->db;
402 int new_fd;
403 struct dotlock *dotlock;
404
405 new_fd = file_dotlock_open(&db->dotlock_set, trans->path, 0, &dotlock);
406 if (new_fd != -1)
407 ;
408 else if (errno != EAGAIN) {
409 e_error(trans->event,
410 "file_dotlock_open(%s) failed: %m", trans->path);
411 } else {
412 e_error(trans->event,
413 "Creating lock file for %s timed out in %u secs",
414 trans->path, db->dotlock_set.timeout);
415 }
416
417 (void)mail_duplicate_read_db_file(trans);
418
419 if (dotlock != NULL)
420 file_dotlock_delete(&dotlock);
421 }
422
mail_duplicate_update(struct mail_duplicate_transaction * trans)423 static void mail_duplicate_update(struct mail_duplicate_transaction *trans)
424 {
425 struct stat st;
426
427 if (stat(trans->path, &st) < 0) {
428 if (errno == ENOENT) {
429 e_debug(trans->event, "DB file not created yet");
430 } else {
431 e_error(trans->event,
432 "stat(%s) failed: %m", trans->path);
433 }
434 } else if (trans->db_ino == st.st_ino) {
435 e_debug(trans->event, "DB file not changed");
436 } else {
437 e_debug(trans->event, "DB file changed: "
438 "Updating duplicate records from DB file");
439
440 mail_duplicate_read(trans);
441 }
442 }
443
444 struct mail_duplicate_transaction *
mail_duplicate_transaction_begin(struct mail_duplicate_db * db)445 mail_duplicate_transaction_begin(struct mail_duplicate_db *db)
446 {
447 struct mail_duplicate_transaction *trans;
448 pool_t pool;
449
450 db->transaction_count++;
451
452 pool = pool_alloconly_create("mail_duplicates", 10240);
453
454 trans = p_new(pool, struct mail_duplicate_transaction, 1);
455 trans->pool = pool;
456 trans->db = db;
457
458 trans->event = event_create(db->event);
459 event_set_append_log_prefix(trans->event, "transaction: ");
460
461 if (db->path == NULL) {
462 /* Duplicate database disabled; return dummy transaction */
463 e_debug(trans->event, "Transaction begin (dummy)");
464 return trans;
465 }
466
467 e_debug(trans->event, "Transaction begin; lock %s", db->path);
468
469 trans->path = p_strdup(pool, db->path);
470 hash_table_create(&trans->hash, pool, 0,
471 mail_duplicate_hash, mail_duplicate_cmp);
472
473 mail_duplicate_read(trans);
474
475 return trans;
476 }
477
478 static void
mail_duplicate_transaction_free(struct mail_duplicate_transaction ** _trans)479 mail_duplicate_transaction_free(struct mail_duplicate_transaction **_trans)
480 {
481 struct mail_duplicate_transaction *trans = *_trans;
482 struct hash_iterate_context *iter;
483 struct mail_duplicate *d;
484
485 if (trans == NULL)
486 return;
487 *_trans = NULL;
488
489 e_debug(trans->event, "Transaction free");
490
491 i_assert(trans->db->transaction_count > 0);
492 trans->db->transaction_count--;
493
494 iter = hash_table_iterate_init(trans->hash);
495 while (hash_table_iterate(iter, trans->hash, &d, &d))
496 mail_duplicate_unlock(trans, d);
497 hash_table_iterate_deinit(&iter);
498 i_assert(trans->id_lock_count == 0);
499
500 hash_table_destroy(&trans->hash);
501 event_unref(&trans->event);
502 pool_unref(&trans->pool);
503 }
504
505 static struct mail_duplicate *
mail_duplicate_get(struct mail_duplicate_transaction * trans,const void * id,size_t id_size,const char * user)506 mail_duplicate_get(struct mail_duplicate_transaction *trans,
507 const void *id, size_t id_size, const char *user)
508 {
509 struct mail_duplicate dup_q, *dup;
510
511 dup_q.id = id;
512 dup_q.id_size = id_size;
513 dup_q.user = user;
514
515 dup = hash_table_lookup(trans->hash, &dup_q);
516 if (dup == NULL) {
517 dup = p_new(trans->pool, struct mail_duplicate, 1);
518 dup->id = p_memdup(trans->pool, id, id_size);
519 dup->id_size = id_size;
520 dup->user = p_strdup(trans->pool, user);
521 dup->time = (time_t)-1;
522
523 hash_table_insert(trans->hash, dup, dup);
524 }
525
526 return dup;
527 }
528
529 enum mail_duplicate_check_result
mail_duplicate_check(struct mail_duplicate_transaction * trans,const void * id,size_t id_size,const char * user)530 mail_duplicate_check(struct mail_duplicate_transaction *trans,
531 const void *id, size_t id_size, const char *user)
532 {
533 struct mail_duplicate *dup;
534
535 if (trans->path == NULL) {
536 /* Duplicate database disabled */
537 e_debug(trans->event, "Check ID (dummy)");
538 return MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND;
539 }
540
541 dup = mail_duplicate_get(trans, id, id_size, user);
542
543 switch (mail_duplicate_lock(trans, dup)) {
544 case MAIL_DUPLICATE_LOCK_OK:
545 break;
546 case MAIL_DUPLICATE_LOCK_IO_ERROR:
547 e_debug(trans->event,
548 "Check ID: I/O error occurred while locking");
549 return MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR;
550 case MAIL_DUPLICATE_LOCK_TIMEOUT:
551 e_debug(trans->event,
552 "Check ID: lock timed out");
553 return MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT;
554 case MAIL_DUPLICATE_LOCK_TOO_MANY:
555 e_debug(trans->event,
556 "Check ID: too many IDs locked");
557 return MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS;
558 case MAIL_DUPLICATE_LOCK_DEADLOCK:
559 e_debug(trans->event,
560 "Check ID: deadlock detected while locking");
561 return MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK;
562 }
563
564 mail_duplicate_update(trans);
565 if (dup->marked) {
566 e_debug(trans->event, "Check ID: found");
567 return MAIL_DUPLICATE_CHECK_RESULT_EXISTS;
568 }
569
570 e_debug(trans->event, "Check ID: not found");
571 return MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND;
572 }
573
mail_duplicate_mark(struct mail_duplicate_transaction * trans,const void * id,size_t id_size,const char * user,time_t timestamp)574 void mail_duplicate_mark(struct mail_duplicate_transaction *trans,
575 const void *id, size_t id_size,
576 const char *user, time_t timestamp)
577 {
578 struct mail_duplicate *dup;
579
580 if (trans->path == NULL) {
581 /* Duplicate database disabled */
582 e_debug(trans->event, "Mark ID (dummy)");
583 return;
584 }
585
586 e_debug(trans->event, "Mark ID");
587
588 dup = mail_duplicate_get(trans, id, id_size, user);
589
590 /* Must already be checked and locked */
591 i_assert(mail_duplicate_is_locked(dup));
592
593 dup->time = timestamp;
594 dup->marked = TRUE;
595 dup->changed = TRUE;
596
597 trans->changed = TRUE;
598 }
599
mail_duplicate_transaction_commit(struct mail_duplicate_transaction ** _trans)600 void mail_duplicate_transaction_commit(
601 struct mail_duplicate_transaction **_trans)
602 {
603 struct mail_duplicate_transaction *trans = *_trans;
604 struct mail_duplicate_file_header hdr;
605 struct mail_duplicate_record_header rec;
606 struct ostream *output;
607 struct hash_iterate_context *iter;
608 struct mail_duplicate *d;
609 int new_fd;
610 struct dotlock *dotlock;
611
612 if (trans == NULL)
613 return;
614 *_trans = NULL;
615
616 if (trans->path == NULL) {
617 e_debug(trans->event, "Commit (dummy)");
618 mail_duplicate_transaction_free(&trans);
619 return;
620 }
621 if (!trans->changed) {
622 e_debug(trans->event, "Commit; no changes");
623 mail_duplicate_transaction_free(&trans);
624 return;
625 }
626
627 struct mail_duplicate_db *db = trans->db;
628
629 i_assert(trans->path != NULL);
630 e_debug(trans->event, "Commit; overwrite %s", trans->path);
631
632 new_fd = file_dotlock_open(&db->dotlock_set, trans->path, 0, &dotlock);
633 if (new_fd != -1)
634 ;
635 else if (errno != EAGAIN) {
636 e_error(trans->event,
637 "file_dotlock_open(%s) failed: %m",
638 trans->path);
639 mail_duplicate_transaction_free(&trans);
640 return;
641 } else {
642 e_error(trans->event,
643 "Creating lock file for %s timed out in %u secs",
644 trans->path, db->dotlock_set.timeout);
645 mail_duplicate_transaction_free(&trans);
646 return;
647 }
648
649 i_zero(&hdr);
650 hdr.version = DUPLICATE_VERSION;
651
652 output = o_stream_create_fd_file(new_fd, 0, FALSE);
653 o_stream_cork(output);
654 o_stream_nsend(output, &hdr, sizeof(hdr));
655
656 i_zero(&rec);
657 iter = hash_table_iterate_init(trans->hash);
658 while (hash_table_iterate(iter, trans->hash, &d, &d)) {
659 if (d->marked) {
660 rec.stamp = d->time;
661 rec.id_size = d->id_size;
662 rec.user_size = strlen(d->user);
663
664 o_stream_nsend(output, &rec, sizeof(rec));
665 o_stream_nsend(output, d->id, rec.id_size);
666 o_stream_nsend(output, d->user, rec.user_size);
667 }
668 }
669 hash_table_iterate_deinit(&iter);
670
671 if (o_stream_finish(output) < 0) {
672 e_error(trans->event, "write(%s) failed: %s",
673 trans->path, o_stream_get_error(output));
674 o_stream_unref(&output);
675 mail_duplicate_transaction_free(&trans);
676 return;
677 }
678 o_stream_unref(&output);
679
680 if (file_dotlock_replace(&dotlock, 0) < 0) {
681 e_error(trans->event,
682 "file_dotlock_replace(%s) failed: %m", trans->path);
683 }
684
685 iter = hash_table_iterate_init(trans->hash);
686 while (hash_table_iterate(iter, trans->hash, &d, &d))
687 mail_duplicate_unlock(trans, d);
688 hash_table_iterate_deinit(&iter);
689
690 mail_duplicate_transaction_free(&trans);
691 }
692
mail_duplicate_transaction_rollback(struct mail_duplicate_transaction ** _trans)693 void mail_duplicate_transaction_rollback(
694 struct mail_duplicate_transaction **_trans)
695 {
696 struct mail_duplicate_transaction *trans = *_trans;
697
698 if (trans == NULL)
699 return;
700 *_trans = NULL;
701
702 if (trans->path == NULL)
703 e_debug(trans->event, "Rollback (dummy)");
704 else
705 e_debug(trans->event, "Rollback");
706
707 mail_duplicate_transaction_free(&trans);
708 }
709
710 struct mail_duplicate_db *
mail_duplicate_db_init(struct mail_user * user,const char * name)711 mail_duplicate_db_init(struct mail_user *user, const char *name)
712 {
713 struct mail_duplicate_db *db;
714 const struct mail_storage_settings *mail_set;
715 const char *home = NULL;
716 const char *lock_dir;
717
718 db = i_new(struct mail_duplicate_db, 1);
719
720 db->event = event_create(user->event);
721 event_set_append_log_prefix(db->event, "duplicate db: ");
722
723 e_debug(db->event, "Initialize");
724
725 if (mail_user_get_home(user, &home) <= 0) {
726 e_error(db->event, "User %s doesn't have home dir set, "
727 "disabling duplicate database", user->username);
728 }
729
730 db->user = user;
731 db->path = home == NULL ? NULL :
732 i_strconcat(home, "/.dovecot.", name, NULL);
733 db->dotlock_set = default_mail_duplicate_dotlock_set;
734
735 lock_dir = mail_user_get_volatile_dir(user);
736 if (lock_dir == NULL)
737 lock_dir = home;
738 db->lock_dir = i_strconcat(lock_dir, "/.dovecot.", name, ".locks",
739 NULL);
740
741 mail_set = mail_user_set_get_storage_set(user);
742 db->dotlock_set.use_excl_lock = mail_set->dotlock_use_excl;
743 db->dotlock_set.nfs_flush = mail_set->mail_nfs_storage;
744
745 return db;
746 }
747
mail_duplicate_db_deinit(struct mail_duplicate_db ** _db)748 void mail_duplicate_db_deinit(struct mail_duplicate_db **_db)
749 {
750 struct mail_duplicate_db *db = *_db;
751
752 *_db = NULL;
753
754 e_debug(db->event, "Cleanup");
755
756 i_assert(db->transaction_count == 0);
757
758 event_unref(&db->event);
759 i_free(db->path);
760 i_free(db->lock_dir);
761 i_free(db);
762 }
763