Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/lib-storage/index/maildir/maildir-sync.c @ 1915:79790750c349 HEAD
importing new index code. mbox still broken.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 27 Apr 2004 23:25:52 +0300 |
parents | |
children | 68938dccbc45 |
comparison
equal
deleted
inserted
replaced
1914:1f156d653f4a | 1915:79790750c349 |
---|---|
1 /* | |
2 1. read files in maildir | |
3 2. see if they're all found in uidlist read in memory | |
4 3. if not, check if uidlist's mtime has changed and read it if so | |
5 4. if still not, lock uidlist, sync it once more and generate UIDs for new | |
6 files | |
7 5. apply changes in transaction log | |
8 6. apply changes in maildir to index | |
9 */ | |
10 | |
11 /* Copyright (C) 2004 Timo Sirainen */ | |
12 | |
13 #include "lib.h" | |
14 #include "ioloop.h" | |
15 #include "buffer.h" | |
16 #include "hash.h" | |
17 #include "str.h" | |
18 #include "maildir-storage.h" | |
19 #include "maildir-uidlist.h" | |
20 | |
21 #include <stdio.h> | |
22 #include <unistd.h> | |
23 #include <dirent.h> | |
24 #include <sys/stat.h> | |
25 | |
26 #define MAILDIR_SYNC_SECS 1 | |
27 | |
28 #define MAILDIR_FILENAME_FLAG_FOUND 128 | |
29 | |
30 struct maildir_sync_context { | |
31 struct index_mailbox *ibox; | |
32 const char *new_dir, *cur_dir; | |
33 | |
34 struct maildir_uidlist_sync_ctx *uidlist_sync_ctx; | |
35 }; | |
36 | |
37 static int maildir_expunge(struct index_mailbox *ibox, const char *path, | |
38 void *context __attr_unused__) | |
39 { | |
40 if (unlink(path) == 0) | |
41 return 1; | |
42 if (errno == ENOENT) | |
43 return 0; | |
44 | |
45 mail_storage_set_critical(ibox->box.storage, | |
46 "unlink(%s) failed: %m", path); | |
47 return -1; | |
48 } | |
49 | |
50 static int maildir_sync_flags(struct index_mailbox *ibox, const char *path, | |
51 void *context) | |
52 { | |
53 struct mail_index_sync_rec *syncrec = context; | |
54 const char *newpath; | |
55 enum mail_flags flags; | |
56 uint8_t flags8; | |
57 custom_flags_mask_t custom_flags; | |
58 | |
59 (void)maildir_filename_get_flags(path, &flags, custom_flags); | |
60 | |
61 flags8 = flags; | |
62 mail_index_sync_flags_apply(syncrec, &flags8, custom_flags); | |
63 | |
64 newpath = maildir_filename_set_flags(path, flags8, custom_flags); | |
65 if (rename(path, newpath) == 0) | |
66 return 1; | |
67 if (errno == ENOENT) | |
68 return 0; | |
69 | |
70 mail_storage_set_critical(ibox->box.storage, | |
71 "rename(%s, %s) failed: %m", path, newpath); | |
72 return -1; | |
73 } | |
74 | |
75 static int maildir_sync_record(struct index_mailbox *ibox, | |
76 struct mail_index_view *view, | |
77 struct mail_index_sync_rec *syncrec) | |
78 { | |
79 const struct mail_index_record *rec; | |
80 uint32_t seq, uid; | |
81 | |
82 switch (syncrec->type) { | |
83 case MAIL_INDEX_SYNC_TYPE_APPEND: | |
84 break; | |
85 case MAIL_INDEX_SYNC_TYPE_EXPUNGE: | |
86 for (seq = syncrec->seq1; seq <= syncrec->seq2; seq++) { | |
87 if (mail_index_lookup_uid(view, seq, &uid) < 0) | |
88 return -1; | |
89 if (maildir_file_do(ibox, uid, maildir_expunge, | |
90 NULL) < 0) | |
91 return -1; | |
92 } | |
93 break; | |
94 case MAIL_INDEX_SYNC_TYPE_FLAGS: | |
95 for (seq = syncrec->seq1; seq <= syncrec->seq2; seq++) { | |
96 if (mail_index_lookup_uid(view, seq, &uid) < 0) | |
97 return -1; | |
98 if (maildir_file_do(ibox, uid, maildir_sync_flags, | |
99 syncrec) < 0) | |
100 return -1; | |
101 } | |
102 break; | |
103 } | |
104 | |
105 return 0; | |
106 } | |
107 | |
108 int maildir_sync_last_commit(struct index_mailbox *ibox) | |
109 { | |
110 struct mail_index_view *view; | |
111 struct mail_index_sync_ctx *sync_ctx; | |
112 struct mail_index_sync_rec sync_rec; | |
113 int ret; | |
114 | |
115 if (ibox->commit_log_file_seq == 0) | |
116 return 0; | |
117 | |
118 ret = mail_index_sync_begin(ibox->index, &sync_ctx, &view, | |
119 ibox->commit_log_file_seq, | |
120 ibox->commit_log_file_offset); | |
121 if (ret > 0) { | |
122 while ((ret = mail_index_sync_next(sync_ctx, &sync_rec)) > 0) { | |
123 if (maildir_sync_record(ibox, view, &sync_rec) < 0) { | |
124 ret = -1; | |
125 break; | |
126 } | |
127 } | |
128 if (mail_index_sync_end(sync_ctx) < 0) | |
129 ret = -1; | |
130 } | |
131 | |
132 if (ret == 0) { | |
133 ibox->commit_log_file_seq = 0; | |
134 ibox->commit_log_file_offset = 0; | |
135 } else { | |
136 // FIXME: this is bad - we have to fix this in some way | |
137 mail_storage_set_index_error(ibox); | |
138 } | |
139 return ret; | |
140 } | |
141 | |
142 static struct maildir_sync_context * | |
143 maildir_sync_context_new(struct index_mailbox *ibox) | |
144 { | |
145 struct maildir_sync_context *ctx; | |
146 | |
147 ctx = t_new(struct maildir_sync_context, 1); | |
148 ctx->ibox = ibox; | |
149 ctx->new_dir = t_strconcat(ibox->path, "/new", NULL); | |
150 ctx->cur_dir = t_strconcat(ibox->path, "/cur", NULL); | |
151 return ctx; | |
152 } | |
153 | |
154 static void maildir_sync_deinit(struct maildir_sync_context *ctx) | |
155 { | |
156 if (ctx->uidlist_sync_ctx != NULL) | |
157 (void)maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx); | |
158 } | |
159 | |
160 static int maildir_fix_duplicate(struct index_mailbox *ibox, const char *dir, | |
161 const char *old_fname) | |
162 { | |
163 const char *new_fname, *old_path, *new_path; | |
164 int ret = 0; | |
165 | |
166 t_push(); | |
167 | |
168 old_path = t_strconcat(dir, "/", old_fname, NULL); | |
169 new_fname = maildir_generate_tmp_filename(&ioloop_timeval); | |
170 new_path = t_strconcat(ibox->path, "/new/", new_fname, NULL); | |
171 | |
172 if (rename(old_path, new_path) == 0) { | |
173 i_warning("Fixed duplicate in %s: %s -> %s", | |
174 ibox->path, old_fname, new_fname); | |
175 } else if (errno != ENOENT) { | |
176 mail_storage_set_critical(ibox->box.storage, | |
177 "rename(%s, %s) failed: %m", old_path, new_path); | |
178 ret = -1; | |
179 } | |
180 t_pop(); | |
181 | |
182 return ret; | |
183 } | |
184 | |
185 static int maildir_scan_dir(struct maildir_sync_context *ctx, int new_dir) | |
186 { | |
187 struct mail_storage *storage = ctx->ibox->box.storage; | |
188 const char *dir; | |
189 DIR *dirp; | |
190 string_t *src, *dest; | |
191 struct dirent *dp; | |
192 int move_new, this_new, ret = 1; | |
193 | |
194 src = t_str_new(1024); | |
195 dest = t_str_new(1024); | |
196 | |
197 dir = new_dir ? ctx->new_dir : ctx->cur_dir; | |
198 dirp = opendir(dir); | |
199 if (dirp == NULL) { | |
200 mail_storage_set_critical(storage, | |
201 "opendir(%s) failed: %m", dir); | |
202 return -1; | |
203 } | |
204 | |
205 move_new = new_dir; | |
206 while ((dp = readdir(dirp)) != NULL) { | |
207 if (dp->d_name[0] == '.') | |
208 continue; | |
209 | |
210 this_new = new_dir; | |
211 if (move_new) { | |
212 str_truncate(src, 0); | |
213 str_truncate(dest, 0); | |
214 str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name); | |
215 str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name); | |
216 if (rename(str_c(src), str_c(dest)) == 0 || | |
217 ENOTFOUND(errno)) { | |
218 /* moved - we'll look at it later in cur/ dir */ | |
219 this_new = FALSE; | |
220 continue; | |
221 } else if (ENOSPACE(errno)) { | |
222 /* not enough disk space, leave here */ | |
223 move_new = FALSE; | |
224 } else { | |
225 mail_storage_set_critical(storage, | |
226 "rename(%s, %s) failed: %m", | |
227 str_c(src), str_c(dest)); | |
228 } | |
229 } | |
230 | |
231 ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx, | |
232 dp->d_name, this_new); | |
233 if (ret <= 0) { | |
234 if (ret < 0) | |
235 break; | |
236 | |
237 /* possibly duplicate - try fixing it */ | |
238 if (maildir_fix_duplicate(ctx->ibox, | |
239 dir, dp->d_name) < 0) { | |
240 ret = -1; | |
241 break; | |
242 } | |
243 } | |
244 } | |
245 | |
246 if (closedir(dirp) < 0) { | |
247 mail_storage_set_critical(storage, | |
248 "closedir(%s) failed: %m", dir); | |
249 } | |
250 return ret < 0 ? -1 : 0; | |
251 } | |
252 | |
253 static int maildir_sync_quick_check(struct maildir_sync_context *ctx, | |
254 int *new_changed_r, int *cur_changed_r) | |
255 { | |
256 struct index_mailbox *ibox = ctx->ibox; | |
257 struct stat st; | |
258 time_t new_mtime, cur_mtime; | |
259 | |
260 *new_changed_r = *cur_changed_r = FALSE; | |
261 | |
262 if (stat(ctx->new_dir, &st) < 0) { | |
263 mail_storage_set_critical(ibox->box.storage, | |
264 "stat(%s) failed: %m", ctx->new_dir); | |
265 return -1; | |
266 } | |
267 new_mtime = st.st_mtime; | |
268 | |
269 if (stat(ctx->cur_dir, &st) < 0) { | |
270 mail_storage_set_critical(ibox->box.storage, | |
271 "stat(%s) failed: %m", ctx->cur_dir); | |
272 return -1; | |
273 } | |
274 cur_mtime = st.st_mtime; | |
275 | |
276 if (new_mtime != ibox->last_new_mtime || | |
277 new_mtime >= ibox->last_sync - MAILDIR_SYNC_SECS) { | |
278 *new_changed_r = TRUE; | |
279 ibox->last_new_mtime = new_mtime; | |
280 } | |
281 if (cur_mtime != ibox->last_cur_mtime || | |
282 (cur_mtime >= ibox->last_sync - MAILDIR_SYNC_SECS && | |
283 ioloop_time - ibox->last_sync > MAILDIR_SYNC_SECS)) { | |
284 /* cur/ changed, or delayed cur/ check */ | |
285 *cur_changed_r = TRUE; | |
286 ibox->last_cur_mtime = cur_mtime; | |
287 } | |
288 ibox->last_sync = ioloop_time; | |
289 | |
290 return 0; | |
291 } | |
292 | |
293 static int maildir_sync_index(struct maildir_sync_context *ctx) | |
294 { | |
295 struct index_mailbox *ibox = ctx->ibox; | |
296 struct mail_index_sync_ctx *sync_ctx; | |
297 struct mail_index_sync_rec sync_rec; | |
298 struct maildir_uidlist_iter_ctx *iter; | |
299 struct mail_index_transaction *trans; | |
300 struct mail_index_view *view; | |
301 const struct mail_index_header *hdr; | |
302 const struct mail_index_record *rec; | |
303 uint32_t seq, uid, uflags; | |
304 const char *filename; | |
305 enum mail_flags flags; | |
306 custom_flags_mask_t custom_flags; | |
307 int ret = 0; | |
308 | |
309 if (mail_index_sync_begin(ibox->index, &sync_ctx, &view, | |
310 (uint32_t)-1, (uoff_t)-1) <= 0) { | |
311 // FIXME: ? | |
312 return -1; | |
313 } | |
314 | |
315 hdr = mail_index_get_header(view); | |
316 trans = mail_index_transaction_begin(view, FALSE); | |
317 | |
318 seq = 0; | |
319 iter = maildir_uidlist_iter_init(ibox->uidlist); | |
320 while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) { | |
321 maildir_filename_get_flags(filename, &flags, custom_flags); | |
322 | |
323 __again: | |
324 seq++; | |
325 if (seq > hdr->messages_count) { | |
326 mail_index_append(trans, uid, &seq); | |
327 mail_index_update_flags(trans, seq, MODIFY_REPLACE, | |
328 flags, custom_flags); | |
329 continue; | |
330 } | |
331 | |
332 if (mail_index_lookup(view, seq, &rec) < 0) { | |
333 mail_storage_set_index_error(ibox); | |
334 ret = -1; | |
335 break; | |
336 } | |
337 | |
338 if (rec->uid < uid) { | |
339 /* expunged */ | |
340 mail_index_expunge(trans, seq); | |
341 goto __again; | |
342 } | |
343 | |
344 if (rec->uid > uid) { | |
345 /* new UID in the middle of the mailbox - | |
346 shouldn't happen */ | |
347 mail_storage_set_critical(ibox->box.storage, | |
348 "Maildir sync: UID inserted in the middle " | |
349 "of mailbox (%u > %u)", rec->uid, uid); | |
350 (void)mail_index_reset(ibox->index); | |
351 ret = -1; | |
352 break; | |
353 } | |
354 | |
355 maildir_filename_get_flags(filename, &flags, custom_flags); | |
356 if (rec->flags & MAIL_RECENT) | |
357 flags |= MAIL_RECENT; | |
358 if ((uint8_t)flags != (rec->flags & MAIL_FLAGS_MASK) || | |
359 memcmp(custom_flags, rec->custom_flags, | |
360 INDEX_CUSTOM_FLAGS_BYTE_COUNT) != 0) { | |
361 mail_index_update_flags(trans, seq, MODIFY_REPLACE, | |
362 flags, custom_flags); | |
363 } | |
364 } | |
365 maildir_uidlist_iter_deinit(iter); | |
366 | |
367 if (ret < 0) | |
368 mail_index_transaction_rollback(trans); | |
369 else { | |
370 uint32_t seq; | |
371 uoff_t offset; | |
372 | |
373 if (mail_index_transaction_commit(trans, &seq, &offset) < 0) | |
374 mail_storage_set_index_error(ibox); | |
375 else { | |
376 ibox->commit_log_file_seq = seq; | |
377 ibox->commit_log_file_offset = offset; | |
378 } | |
379 } | |
380 | |
381 /* now, sync the index */ | |
382 while ((ret = mail_index_sync_next(sync_ctx, &sync_rec)) > 0) { | |
383 if (maildir_sync_record(ibox, view, &sync_rec) < 0) { | |
384 ret = -1; | |
385 break; | |
386 } | |
387 } | |
388 if (mail_index_sync_end(sync_ctx) < 0) | |
389 ret = -1; | |
390 | |
391 if (ret == 0) { | |
392 ibox->commit_log_file_seq = 0; | |
393 ibox->commit_log_file_offset = 0; | |
394 } else { | |
395 // FIXME: this is bad - we have to fix this in some way | |
396 mail_storage_set_index_error(ibox); | |
397 } | |
398 | |
399 return ret; | |
400 } | |
401 | |
402 static int maildir_sync_context(struct maildir_sync_context *ctx, | |
403 int *changes_r) | |
404 { | |
405 int ret, new_changed, cur_changed; | |
406 | |
407 if (maildir_sync_quick_check(ctx, &new_changed, &cur_changed) < 0) | |
408 return -1; | |
409 | |
410 ctx->uidlist_sync_ctx = maildir_uidlist_sync_init(ctx->ibox->uidlist); | |
411 | |
412 if (maildir_scan_dir(ctx, TRUE) < 0) | |
413 return -1; | |
414 if (maildir_scan_dir(ctx, FALSE) < 0) | |
415 return -1; | |
416 | |
417 ret = maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx); | |
418 ctx->uidlist_sync_ctx = NULL; | |
419 | |
420 if (ret == 0) | |
421 ret = maildir_sync_index(ctx); | |
422 return ret; | |
423 } | |
424 | |
425 static int maildir_sync_context_readonly(struct maildir_sync_context *ctx) | |
426 { | |
427 int ret; | |
428 | |
429 ctx->uidlist_sync_ctx = maildir_uidlist_sync_init(ctx->ibox->uidlist); | |
430 | |
431 if (maildir_scan_dir(ctx, TRUE) < 0) | |
432 return -1; | |
433 if (maildir_scan_dir(ctx, FALSE) < 0) | |
434 return -1; | |
435 | |
436 ret = maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx); | |
437 ctx->uidlist_sync_ctx = NULL; | |
438 | |
439 return ret; | |
440 } | |
441 | |
442 int maildir_storage_sync_readonly(struct index_mailbox *ibox) | |
443 { | |
444 struct maildir_sync_context *ctx; | |
445 int ret; | |
446 | |
447 ctx = maildir_sync_context_new(ibox); | |
448 ret = maildir_sync_context_readonly(ctx); | |
449 maildir_sync_deinit(ctx); | |
450 return ret; | |
451 } | |
452 | |
453 int maildir_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags) | |
454 { | |
455 struct index_mailbox *ibox = (struct index_mailbox *)box; | |
456 struct maildir_sync_context *ctx; | |
457 int changes, ret; | |
458 | |
459 if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 || | |
460 ibox->sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <= ioloop_time) { | |
461 ibox->sync_last_check = ioloop_time; | |
462 | |
463 ctx = maildir_sync_context_new(ibox); | |
464 ret = maildir_sync_context(ctx, &changes); | |
465 maildir_sync_deinit(ctx); | |
466 | |
467 if (ret < 0) | |
468 return -1; | |
469 } | |
470 | |
471 return index_storage_sync(box, flags); | |
472 } |