Coverage Report

Created: 2026-06-15 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-storage/index/index-transaction.c
Line
Count
Source
1
/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "dict.h"
6
#include "index-storage.h"
7
#include "index-sync-private.h"
8
#include "index-pop3-uidl.h"
9
#include "index-mail.h"
10
11
static void index_transaction_free(struct mailbox_transaction_context *t)
12
0
{
13
0
  if (t->view_pvt != NULL)
14
0
    mail_index_view_close(&t->view_pvt);
15
0
  mail_cache_view_close(&t->cache_view);
16
0
  mail_index_view_close(&t->view);
17
0
  if (array_is_created(&t->pvt_saves))
18
0
    array_free(&t->pvt_saves);
19
0
  array_free(&t->module_contexts);
20
0
  i_free(t->reason);
21
0
  i_free(t);
22
0
}
23
24
static int
25
index_transaction_index_commit(struct mail_index_transaction *index_trans,
26
             struct mail_index_transaction_commit_result *result_r)
27
0
{
28
0
  struct mailbox_transaction_context *t =
29
0
    MAIL_STORAGE_CONTEXT_REQUIRE(index_trans);
30
0
  struct index_mailbox_sync_pvt_context *pvt_sync_ctx = NULL;
31
0
  const char *error;
32
0
  int ret = 0;
33
34
0
  index_pop3_uidl_update_exists_finish(t);
35
36
0
  if (t->attr_pvt_trans != NULL) {
37
0
    if (dict_transaction_commit(&t->attr_pvt_trans, &error) < 0) {
38
0
      mailbox_set_critical(t->box,
39
0
        "Dict private transaction commit failed: %s", error);
40
0
      ret = -1;
41
0
    }
42
0
  }
43
0
  if (t->attr_shared_trans != NULL) {
44
0
    if (dict_transaction_commit(&t->attr_shared_trans, &error) < 0) {
45
0
      mailbox_set_critical(t->box,
46
0
        "Dict shared transaction commit failed: %s", error);
47
0
      ret = -1;
48
0
    }
49
0
  }
50
51
0
  if (t->save_ctx != NULL) {
52
0
    mailbox_save_context_deinit(t->save_ctx);
53
0
    if (ret < 0) {
54
0
      t->box->v.transaction_save_rollback(t->save_ctx);
55
0
      t->save_ctx = NULL;
56
0
    } else if (t->box->v.transaction_save_commit_pre(t->save_ctx) < 0) {
57
0
      t->save_ctx = NULL;
58
0
      ret = -1;
59
0
    }
60
0
  }
61
62
0
  if (array_is_created(&t->pvt_saves)) {
63
0
    if (index_mailbox_sync_pvt_init(t->box, TRUE, 0, &pvt_sync_ctx) < 0)
64
0
      ret = -1;
65
0
  }
66
67
0
  i_assert(t->mail_ref_count == 0);
68
0
  if (ret < 0)
69
0
    t->super.rollback(index_trans);
70
0
  else {
71
0
    if (t->super.commit(index_trans, result_r) < 0) {
72
0
      mailbox_set_index_error(t->box);
73
0
      ret = -1;
74
0
    } else {
75
0
      t->changes->changes_mask = result_r->changes_mask;
76
0
    }
77
0
  }
78
79
0
  if (t->save_ctx == NULL) {
80
0
  } else if (ret >= 0) {
81
0
    i_assert(t->save_ctx->dest_mail == NULL);
82
0
    t->box->v.transaction_save_commit_post(t->save_ctx, result_r);
83
0
  } else {
84
0
    t->box->v.transaction_save_rollback(t->save_ctx);
85
0
  }
86
87
0
  if (pvt_sync_ctx != NULL) {
88
0
    if (index_mailbox_sync_pvt_newmails(pvt_sync_ctx, t) < 0) {
89
      /* failed to add private flags. a bit too late to
90
         return failure though, so just ignore silently */
91
0
    }
92
0
    index_mailbox_sync_pvt_deinit(&pvt_sync_ctx);
93
0
  }
94
95
0
  if (ret < 0)
96
0
    mail_index_set_error_nolog(t->box->index, mailbox_get_last_error(t->box, NULL));
97
0
  index_transaction_free(t);
98
0
  return ret;
99
0
}
100
101
static void
102
index_transaction_index_rollback(struct mail_index_transaction *index_trans)
103
0
{
104
0
  struct mailbox_transaction_context *t =
105
0
    MAIL_STORAGE_CONTEXT_REQUIRE(index_trans);
106
107
0
  dict_transaction_rollback(&t->attr_pvt_trans);
108
0
  dict_transaction_rollback(&t->attr_shared_trans);
109
110
0
  if (t->save_ctx != NULL) {
111
0
    mailbox_save_context_deinit(t->save_ctx);
112
0
    t->box->v.transaction_save_rollback(t->save_ctx);
113
0
  }
114
115
0
  i_assert(t->mail_ref_count == 0);
116
0
  t->super.rollback(index_trans);
117
0
  index_transaction_free(t);
118
0
}
119
120
static enum mail_index_transaction_flags
121
index_transaction_flags_get(enum mailbox_transaction_flags flags)
122
0
{
123
0
  enum mail_index_transaction_flags itrans_flags;
124
125
0
  itrans_flags = MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES;
126
0
  if ((flags & MAILBOX_TRANSACTION_FLAG_HIDE) != 0)
127
0
    itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_HIDE;
128
0
  if ((flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0)
129
0
    itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL;
130
0
  if ((flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0)
131
0
    itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_SYNC;
132
0
  return itrans_flags;
133
0
}
134
135
void index_transaction_init_pvt(struct mailbox_transaction_context *t)
136
0
{
137
0
  enum mail_index_transaction_flags itrans_flags;
138
139
0
  if (t->box->view_pvt == NULL || t->itrans_pvt != NULL)
140
0
    return;
141
142
0
  itrans_flags = index_transaction_flags_get(t->flags);
143
0
  t->itrans_pvt = mail_index_transaction_begin(t->box->view_pvt,
144
0
                 itrans_flags);
145
0
  t->view_pvt = mail_index_transaction_open_updated_view(t->itrans_pvt);
146
0
}
147
148
void index_transaction_init(struct mailbox_transaction_context *t,
149
          struct mailbox *box,
150
          enum mailbox_transaction_flags flags,
151
          const char *reason)
152
0
{
153
0
  enum mail_index_transaction_flags itrans_flags;
154
155
0
  i_assert(box->opened);
156
157
0
  itrans_flags = index_transaction_flags_get(flags);
158
0
  if ((flags & MAILBOX_TRANSACTION_FLAG_REFRESH) != 0)
159
0
    mail_index_refresh(box->index);
160
161
0
  t->flags = flags;
162
0
  t->box = box;
163
0
  t->reason = i_strdup(reason);
164
0
  t->itrans = mail_index_transaction_begin(box->view, itrans_flags);
165
0
  t->view = mail_index_transaction_open_updated_view(t->itrans);
166
167
0
  array_create(&t->module_contexts, default_pool,
168
0
         sizeof(void *), 5);
169
170
0
  t->cache_view = mail_cache_view_open(box->cache, t->view);
171
0
  t->cache_trans = mail_cache_get_transaction(t->cache_view, t->itrans);
172
173
0
  if ((flags & MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC) != 0)
174
0
    mail_cache_view_update_cache_decisions(t->cache_view, FALSE);
175
176
  /* set up after mail_cache_get_transaction(), so that we'll still
177
     have the cache_trans available in _index_commit() */
178
0
  t->super = t->itrans->v;
179
0
  t->itrans->v.commit = index_transaction_index_commit;
180
0
  t->itrans->v.rollback = index_transaction_index_rollback;
181
0
  MODULE_CONTEXT_SET(t->itrans, mail_storage_mail_index_module, t);
182
0
}
183
184
struct mailbox_transaction_context *
185
index_transaction_begin(struct mailbox *box,
186
      enum mailbox_transaction_flags flags,
187
      const char *reason)
188
0
{
189
0
  struct mailbox_transaction_context *t;
190
191
0
  t = i_new(struct mailbox_transaction_context, 1);
192
0
  index_transaction_init(t, box, flags, reason);
193
0
  return t;
194
0
}
195
196
int index_transaction_commit(struct mailbox_transaction_context *t,
197
           struct mail_transaction_commit_changes *changes_r)
198
0
{
199
0
  struct mailbox *box = t->box;
200
0
  struct mail_index_transaction *itrans = t->itrans;
201
0
  struct mail_index_transaction_commit_result result;
202
0
  int ret = 0;
203
204
0
  i_zero(changes_r);
205
0
  changes_r->pool = pool_alloconly_create(MEMPOOL_GROWING
206
0
            "transaction changes", 512);
207
0
  p_array_init(&changes_r->saved_uids, changes_r->pool, 32);
208
0
  t->changes = changes_r;
209
210
0
  if (t->itrans_pvt != NULL)
211
0
    ret = mail_index_transaction_commit(&t->itrans_pvt);
212
0
  if (mail_index_transaction_commit_full(&itrans, &result) < 0)
213
0
    ret = -1;
214
0
  t = NULL;
215
216
0
  if (ret < 0 && mail_index_is_deleted(box->index))
217
0
    mailbox_set_deleted(box);
218
219
0
  changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
220
0
  return ret;
221
0
}
222
223
void index_transaction_rollback(struct mailbox_transaction_context *t)
224
0
{
225
0
  struct mail_index_transaction *itrans = t->itrans;
226
227
0
  if (t->itrans_pvt != NULL)
228
0
    mail_index_transaction_rollback(&t->itrans_pvt);
229
0
  mail_index_transaction_rollback(&itrans);
230
0
}