Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-index/mail-modifylog.c @ 160:ff05b320482c HEAD
Bigger changes.. full_virtual_size was removed from index record and
MessagePart caching is now forced. Also added per-message flags, including
binary flags which can be used to check if CRs need to be inserted into
message data.
Added mbox-rewrite support which can be used to write out mbox file with
updated flags. This still has the problem of being able to read changed
custom flags, that'll require another bigger change.
There's also several other mostly mbox related fixes.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 06 Sep 2002 16:43:58 +0300 |
parents | 24c8fcb78e0e |
children | cf1279829ad1 |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2002 Timo Sirainen */ |
2 | |
3 #include "lib.h" | |
4 #include "mmap-util.h" | |
29
e9375147c0cb
Added write_full() which is a simple wrapper around write() meant for
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
5 #include "write-full.h" |
0 | 6 #include "mail-index.h" |
7 #include "mail-index-util.h" | |
8 #include "mail-modifylog.h" | |
9 | |
10 #include <stdlib.h> | |
11 #include <fcntl.h> | |
12 | |
13 /* Maximum size for modify log (isn't exact) */ | |
14 #define MAX_MODIFYLOG_SIZE 10240 | |
15 | |
16 #define MODIFYLOG_FILE_POSITION(log, ptr) \ | |
17 ((int) ((char *) (ptr) - (char *) (log)->mmap_base)) | |
18 | |
19 struct _MailModifyLog { | |
20 MailIndex *index; | |
21 | |
22 int fd; | |
23 char *filepath; | |
24 | |
25 void *mmap_base; | |
26 size_t mmap_length; | |
27 | |
28 ModifyLogHeader *header; | |
29 size_t synced_position; | |
30 unsigned int synced_id, mmaped_id; | |
31 | |
32 unsigned int modified:1; | |
33 unsigned int dirty_mmap:1; | |
34 unsigned int second_log:1; | |
35 }; | |
36 | |
37 static int file_lock(int fd, int wait_lock, int lock_type) | |
38 { | |
39 struct flock fl; | |
40 | |
41 fl.l_type = lock_type; | |
42 fl.l_whence = SEEK_SET; | |
43 fl.l_start = 0; | |
44 fl.l_len = 0; | |
45 | |
46 if (fcntl(fd, wait_lock ? F_SETLKW : F_SETLK, &fl) == -1) { | |
130
c2ec223e37ba
mail_modifylog_try_lock() didn't check for EAGAIN error.
Timo Sirainen <tss@iki.fi>
parents:
115
diff
changeset
|
47 if (errno == EACCES || errno == EAGAIN) |
0 | 48 return 0; |
49 return -1; | |
50 } | |
51 | |
52 return 1; | |
53 } | |
54 | |
55 /* Returns 1 = ok, 0 = failed to get the lock, -1 = error */ | |
56 static int mail_modifylog_try_lock(MailModifyLog *log, int lock_type) | |
57 { | |
58 int ret; | |
59 | |
60 ret = file_lock(log->fd, FALSE, lock_type); | |
61 if (ret == -1) { | |
62 index_set_error(log->index, "fcntl() failed with file %s: %m", | |
63 log->filepath); | |
64 } | |
65 | |
66 return ret; | |
67 } | |
68 | |
69 static int mail_modifylog_wait_lock(MailModifyLog *log) | |
70 { | |
71 if (file_lock(log->fd, TRUE, F_RDLCK) < 1) { | |
72 index_set_error(log->index, "fcntl() failed with file %s: %m", | |
73 log->filepath); | |
74 return FALSE; | |
75 } | |
76 | |
77 return TRUE; | |
78 } | |
79 | |
80 /* returns 1 = yes, 0 = no, -1 = error */ | |
81 static int mail_modifylog_have_other_users(MailModifyLog *log) | |
82 { | |
83 int ret; | |
84 | |
85 /* try grabbing exclusive lock */ | |
86 ret = mail_modifylog_try_lock(log, F_WRLCK); | |
87 if (ret == -1) | |
88 return -1; | |
89 | |
90 /* revert back to shared lock */ | |
135
24c8fcb78e0e
"revert back to shared lock" actually tried to set exclusive lock again
Timo Sirainen <tss@iki.fi>
parents:
130
diff
changeset
|
91 switch (mail_modifylog_try_lock(log, F_RDLCK)) { |
0 | 92 case 0: |
93 /* shouldn't happen */ | |
94 index_set_error(log->index, "fcntl(F_WRLCK -> F_RDLCK) " | |
95 "failed with file %s", log->filepath); | |
96 /* fall through */ | |
97 case -1: | |
98 return -1; | |
99 } | |
100 | |
101 return ret == 0 ? 1 : 0; | |
102 } | |
103 | |
104 static int mmap_update(MailModifyLog *log) | |
105 { | |
106 unsigned int extra; | |
107 | |
108 if (!log->dirty_mmap && log->mmaped_id == log->header->sync_id) | |
109 return TRUE; | |
110 | |
111 if (log->mmap_base != NULL) | |
112 (void)munmap(log->mmap_base, log->mmap_length); | |
113 | |
114 log->mmap_base = mmap_rw_file(log->fd, &log->mmap_length); | |
115 if (log->mmap_base == MAP_FAILED) { | |
116 log->mmap_base = NULL; | |
117 log->header = NULL; | |
118 index_set_error(log->index, | |
119 "modify log: mmap() failed with file %s: %m", | |
120 log->filepath); | |
121 return FALSE; | |
122 } | |
123 | |
124 if (log->mmap_length < sizeof(ModifyLogHeader)) { | |
125 /* FIXME: we could do better.. */ | |
126 (void)unlink(log->filepath); | |
127 i_assert(0); | |
128 } | |
129 | |
130 extra = (log->mmap_length - sizeof(ModifyLogHeader)) % | |
131 sizeof(ModifyLogRecord); | |
132 | |
133 if (extra != 0) { | |
134 /* partial write or corrupted - | |
135 truncate the file to valid length */ | |
136 log->mmap_length -= extra; | |
50
d493b9cc265e
Introduced uoff_t which is the unsigned-equilevant of off_t. This was needed
Timo Sirainen <tss@iki.fi>
parents:
29
diff
changeset
|
137 (void)ftruncate(log->fd, (off_t)log->mmap_length); |
0 | 138 } |
139 | |
140 log->dirty_mmap = FALSE; | |
141 log->header = log->mmap_base; | |
142 log->mmaped_id = log->header->sync_id; | |
143 return TRUE; | |
144 } | |
145 | |
146 static MailModifyLog *mail_modifylog_new(MailIndex *index) | |
147 { | |
148 MailModifyLog *log; | |
149 | |
150 log = i_new(MailModifyLog, 1); | |
151 log->fd = -1; | |
152 log->index = index; | |
153 log->dirty_mmap = TRUE; | |
154 | |
155 index->modifylog = log; | |
156 return log; | |
157 } | |
158 | |
159 static void mail_modifylog_close(MailModifyLog *log) | |
160 { | |
161 log->dirty_mmap = TRUE; | |
162 | |
163 if (log->mmap_base != NULL) { | |
164 munmap(log->mmap_base, log->mmap_length); | |
165 log->mmap_base = NULL; | |
166 } | |
167 | |
168 if (log->fd != -1) { | |
169 (void)close(log->fd); | |
170 log->fd = -1; | |
171 } | |
172 | |
173 i_free(log->filepath); | |
174 } | |
175 | |
176 static int mail_modifylog_init_fd(MailModifyLog *log, int fd, | |
177 const char *path) | |
178 { | |
179 ModifyLogHeader hdr; | |
180 | |
181 /* write header */ | |
182 memset(&hdr, 0, sizeof(hdr)); | |
183 hdr.indexid = log->index->indexid; | |
184 | |
29
e9375147c0cb
Added write_full() which is a simple wrapper around write() meant for
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
185 if (write_full(fd, &hdr, sizeof(hdr)) < 0) { |
0 | 186 index_set_error(log->index, "write() failed for modify " |
187 "log %s: %m", path); | |
188 return FALSE; | |
189 } | |
190 | |
191 if (ftruncate(fd, sizeof(hdr)) == -1) { | |
192 index_set_error(log->index, "ftruncate() failed for modify " | |
193 "log %s: %m", path); | |
194 return FALSE; | |
195 } | |
196 | |
197 return TRUE; | |
198 } | |
199 | |
200 static int mail_modifylog_open_and_init_file(MailModifyLog *log, | |
201 const char *path) | |
202 { | |
203 int fd, ret; | |
204 | |
205 fd = open(path, O_RDWR | O_CREAT, 0660); | |
206 if (fd == -1) { | |
207 index_set_error(log->index, "Error opening modify log " | |
208 "file %s: %m", path); | |
209 return FALSE; | |
210 } | |
211 | |
212 ret = file_lock(fd, FALSE, F_WRLCK); | |
213 if (ret == -1) { | |
214 index_set_error(log->index, "Error locking modify log " | |
215 "file %s: %m", path); | |
216 } | |
217 | |
218 if (ret == 1 && mail_modifylog_init_fd(log, fd, path)) { | |
219 mail_modifylog_close(log); | |
220 | |
221 log->fd = fd; | |
222 log->filepath = i_strdup(path); | |
223 return TRUE; | |
224 } | |
225 | |
226 (void)close(fd); | |
227 return FALSE; | |
228 } | |
229 | |
230 int mail_modifylog_create(MailIndex *index) | |
231 { | |
232 MailModifyLog *log; | |
233 const char *path; | |
234 | |
235 i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE); | |
236 | |
237 log = mail_modifylog_new(index); | |
238 | |
239 path = t_strconcat(log->index->filepath, ".log", NULL); | |
240 if (!mail_modifylog_open_and_init_file(log, path) || | |
241 !mail_modifylog_wait_lock(log) || | |
242 !mmap_update(log)) { | |
243 /* fatal failure */ | |
244 mail_modifylog_free(log); | |
245 return FALSE; | |
246 } | |
247 | |
248 log->synced_id = log->header->sync_id; | |
249 log->synced_position = log->mmap_length; | |
250 return TRUE; | |
251 } | |
252 | |
253 /* Returns 1 = ok, 0 = full, -1 = error */ | |
254 static int mail_modifylog_open_and_verify(MailModifyLog *log, const char *path) | |
255 { | |
256 ModifyLogHeader hdr; | |
257 int fd, ret; | |
258 | |
259 fd = open(path, O_RDWR); | |
260 if (fd == -1) { | |
261 if (errno != ENOENT) { | |
262 index_set_error(log->index, "Can't open modify log " | |
263 "file %s: %m", path); | |
264 } | |
265 return -1; | |
266 } | |
267 | |
268 ret = 1; | |
269 if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { | |
270 index_set_error(log->index, "read() failed when for modify " | |
271 "log file %s: %m", path); | |
272 ret = -1; | |
273 } | |
274 | |
275 if (ret != -1 && hdr.indexid != log->index->indexid) { | |
276 index_set_error(log->index, "IndexID mismatch for modify log " | |
277 "file %s", path); | |
278 ret = -1; | |
115 | 279 |
280 /* we have to rebuild it, make sure it's deleted. */ | |
281 (void)unlink(path); | |
0 | 282 } |
283 | |
284 if (ret != -1 && hdr.sync_id == SYNC_ID_FULL) { | |
285 /* full */ | |
286 ret = 0; | |
287 } | |
288 | |
289 if (ret == 1) { | |
290 log->fd = fd; | |
291 log->filepath = i_strdup(path); | |
292 } else { | |
293 (void)close(fd); | |
294 } | |
295 | |
296 return ret; | |
297 } | |
298 | |
299 static int mail_modifylog_find_or_create(MailModifyLog *log) | |
300 { | |
301 const char *path1, *path2; | |
115 | 302 int i, ret; |
0 | 303 |
304 for (i = 0; i < 2; i++) { | |
305 /* first try <index>.log */ | |
306 path1 = t_strconcat(log->index->filepath, ".log", NULL); | |
115 | 307 path2 = t_strconcat(log->index->filepath, ".log.2", NULL); |
308 | |
309 ret = mail_modifylog_open_and_verify(log, path1); | |
310 if (ret == 1) | |
0 | 311 return TRUE; |
312 | |
115 | 313 if (ret == 0) { |
314 /* then <index>.log.2 */ | |
315 if (mail_modifylog_open_and_verify(log, path2) == 1) | |
316 return TRUE; | |
317 } | |
0 | 318 |
319 /* try creating/reusing them */ | |
320 if (mail_modifylog_open_and_init_file(log, path1)) | |
321 return TRUE; | |
322 | |
323 if (mail_modifylog_open_and_init_file(log, path2)) | |
324 return TRUE; | |
325 | |
326 /* maybe the file was just switched, check the logs again */ | |
327 } | |
328 | |
329 index_set_error(log->index, "We could neither use nor create " | |
330 "the modify log for index %s", log->index->filepath); | |
331 return FALSE; | |
332 } | |
333 | |
334 int mail_modifylog_open_or_create(MailIndex *index) | |
335 { | |
336 MailModifyLog *log; | |
337 | |
338 log = mail_modifylog_new(index); | |
339 | |
340 if (!mail_modifylog_find_or_create(log) || | |
341 !mail_modifylog_wait_lock(log) || | |
342 !mmap_update(log)) { | |
343 /* fatal failure */ | |
344 mail_modifylog_free(log); | |
345 return FALSE; | |
346 } | |
347 | |
348 log->synced_id = log->header->sync_id; | |
349 log->synced_position = log->mmap_length; | |
350 return TRUE; | |
351 } | |
352 | |
353 void mail_modifylog_free(MailModifyLog *log) | |
354 { | |
355 log->index->modifylog = NULL; | |
356 | |
357 mail_modifylog_close(log); | |
358 i_free(log); | |
359 } | |
360 | |
361 int mail_modifylog_sync_file(MailModifyLog *log) | |
362 { | |
363 if (!log->modified) | |
364 return TRUE; | |
365 | |
366 if (log->mmap_base != NULL) { | |
367 if (msync(log->mmap_base, log->mmap_length, MS_SYNC) == -1) { | |
368 index_set_error(log->index, "msync() failed for %s: %m", | |
369 log->filepath); | |
370 return FALSE; | |
371 } | |
372 } | |
373 | |
374 if (fsync(log->fd) == -1) { | |
375 index_set_error(log->index, "fsync() failed for %s: %m", | |
376 log->filepath); | |
377 return FALSE; | |
378 } | |
379 | |
380 log->modified = FALSE; | |
381 return TRUE; | |
382 } | |
383 | |
384 static int mail_modifylog_append(MailModifyLog *log, ModifyLogRecord *rec, | |
385 int external_change) | |
386 { | |
387 i_assert(log->index->lock_type == MAIL_LOCK_EXCLUSIVE); | |
388 i_assert(rec->seq != 0); | |
389 i_assert(rec->uid != 0); | |
390 | |
391 if (!external_change) { | |
392 switch (mail_modifylog_have_other_users(log)) { | |
393 case 0: | |
394 /* we're the only one having this log open, | |
395 no need for modify log. */ | |
396 return TRUE; | |
397 case -1: | |
398 return FALSE; | |
399 } | |
400 } | |
401 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
402 if (lseek(log->fd, 0, SEEK_END) == -1) { |
0 | 403 index_set_error(log->index, "lseek() failed with file %s: %m", |
404 log->filepath); | |
405 return FALSE; | |
406 } | |
407 | |
29
e9375147c0cb
Added write_full() which is a simple wrapper around write() meant for
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
408 if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0) { |
0 | 409 index_set_error(log->index, "Error appending to file %s: %m", |
410 log->filepath); | |
411 return FALSE; | |
412 } | |
413 | |
414 log->header->sync_id++; | |
415 log->modified = TRUE; | |
416 log->dirty_mmap = TRUE; | |
417 | |
418 if (!external_change) { | |
419 log->synced_id = log->header->sync_id; | |
420 log->synced_position += sizeof(ModifyLogRecord); | |
421 } | |
422 return TRUE; | |
423 } | |
424 | |
425 int mail_modifylog_add_expunge(MailModifyLog *log, unsigned int seq, | |
426 unsigned int uid, int external_change) | |
427 { | |
428 ModifyLogRecord rec; | |
429 | |
430 /* expunges must not be added when log isn't synced */ | |
431 i_assert(external_change || log->synced_id == log->header->sync_id); | |
432 | |
433 rec.type = RECORD_TYPE_EXPUNGE; | |
434 rec.seq = seq; | |
435 rec.uid = uid; | |
436 return mail_modifylog_append(log, &rec, external_change); | |
437 } | |
438 | |
439 int mail_modifylog_add_flags(MailModifyLog *log, unsigned int seq, | |
440 unsigned int uid, int external_change) | |
441 { | |
442 ModifyLogRecord rec; | |
443 | |
444 rec.type = RECORD_TYPE_FLAGS_CHANGED; | |
445 rec.seq = seq; | |
446 rec.uid = uid; | |
447 return mail_modifylog_append(log, &rec, external_change); | |
448 } | |
449 | |
450 ModifyLogRecord *mail_modifylog_get_nonsynced(MailModifyLog *log, | |
451 unsigned int *count) | |
452 { | |
453 ModifyLogRecord *rec, *end_rec; | |
454 | |
455 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
456 | |
457 *count = 0; | |
458 if (!mmap_update(log)) | |
459 return NULL; | |
460 | |
461 i_assert(log->synced_position <= log->mmap_length); | |
462 i_assert(log->synced_position >= sizeof(ModifyLogHeader)); | |
463 | |
464 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
465 log->synced_position); | |
466 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
467 log->mmap_length); | |
468 *count = (unsigned int) (end_rec - rec); | |
469 return rec; | |
470 } | |
471 | |
472 static int mail_modifylog_switch_file(MailModifyLog *log) | |
473 { | |
474 MailIndex *index = log->index; | |
475 | |
476 mail_modifylog_free(log); | |
477 return mail_modifylog_open_or_create(index); | |
478 } | |
479 | |
480 static void mail_modifylog_try_switch_file(MailModifyLog *log) | |
481 { | |
482 const char *path; | |
483 | |
484 path = t_strconcat(log->index->filepath, | |
485 log->second_log ? ".log" : ".log.2", NULL); | |
486 | |
160
ff05b320482c
Bigger changes.. full_virtual_size was removed from index record and
Timo Sirainen <tss@iki.fi>
parents:
135
diff
changeset
|
487 if (mail_modifylog_open_and_init_file(log, path)) { |
ff05b320482c
Bigger changes.. full_virtual_size was removed from index record and
Timo Sirainen <tss@iki.fi>
parents:
135
diff
changeset
|
488 /* FIXME: we want to update the _old_ file's header. |
ff05b320482c
Bigger changes.. full_virtual_size was removed from index record and
Timo Sirainen <tss@iki.fi>
parents:
135
diff
changeset
|
489 and this changes the new one. and it's already closed the |
ff05b320482c
Bigger changes.. full_virtual_size was removed from index record and
Timo Sirainen <tss@iki.fi>
parents:
135
diff
changeset
|
490 old one and mmap() is invalid, and we crash here.. */ |
0 | 491 log->header->sync_id = SYNC_ID_FULL; |
160
ff05b320482c
Bigger changes.. full_virtual_size was removed from index record and
Timo Sirainen <tss@iki.fi>
parents:
135
diff
changeset
|
492 } |
0 | 493 } |
494 | |
495 int mail_modifylog_mark_synced(MailModifyLog *log) | |
496 { | |
497 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
498 | |
90
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
499 if (!mmap_update(log)) |
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
500 return FALSE; |
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
501 |
0 | 502 if (log->header->sync_id == SYNC_ID_FULL) { |
503 /* log file is full, switch to next one */ | |
504 return mail_modifylog_switch_file(log); | |
505 } | |
506 | |
507 if (log->synced_id == log->header->sync_id) { | |
508 /* we are already synced */ | |
509 return TRUE; | |
510 } | |
511 | |
512 log->synced_id = log->header->sync_id; | |
513 log->synced_position = log->mmap_length; | |
514 | |
515 log->modified = TRUE; | |
516 | |
517 if (log->mmap_length > MAX_MODIFYLOG_SIZE) { | |
518 /* if the other file isn't locked, switch to it */ | |
519 mail_modifylog_try_switch_file(log); | |
520 return TRUE; | |
521 } | |
522 | |
523 return TRUE; | |
524 } | |
525 | |
526 static int compare_uint(const void *p1, const void *p2) | |
527 { | |
528 const unsigned int *u1 = p1; | |
529 const unsigned int *u2 = p2; | |
530 | |
531 return *u1 < *u2 ? -1 : *u1 > *u2 ? 1 : 0; | |
532 } | |
533 | |
534 const unsigned int * | |
535 mail_modifylog_seq_get_expunges(MailModifyLog *log, | |
536 unsigned int first_seq, | |
537 unsigned int last_seq, | |
538 unsigned int *expunges_before) | |
539 { | |
540 ModifyLogRecord *rec, *end_rec; | |
541 unsigned int last_pos_seq, before, max_records, *arr, *expunges; | |
542 | |
543 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
544 | |
545 *expunges_before = 0; | |
546 | |
547 if (!mmap_update(log)) | |
548 return NULL; | |
549 | |
550 /* find the first expunged message that affects our range */ | |
551 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
552 log->synced_position); | |
553 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
554 log->mmap_length); | |
555 | |
556 while (rec < end_rec) { | |
557 if (rec->type == RECORD_TYPE_EXPUNGE && rec->seq <= last_seq) | |
558 break; | |
559 rec++; | |
560 } | |
561 | |
562 if (rec >= end_rec) { | |
563 /* none found */ | |
564 expunges = t_malloc(sizeof(unsigned int)); | |
565 *expunges = 0; | |
566 return expunges; | |
567 } | |
568 | |
569 /* allocate memory for the returned array. the file size - synced | |
570 position should be quite near the amount of memory we need, unless | |
571 there's lots of FLAGS_CHANGED records which is why there's the | |
572 second check to make sure it's not unneededly large. */ | |
573 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
574 sizeof(ModifyLogRecord); | |
575 if (max_records > last_seq - first_seq + 1) | |
576 max_records = last_seq - first_seq + 1; | |
577 | |
578 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
579 | |
580 /* last_pos_seq is updated all the time to contain the last_seq | |
581 comparable to current record's seq. number */ | |
582 last_pos_seq = last_seq; | |
583 | |
584 before = 0; | |
585 for (; rec < end_rec; rec++) { | |
586 if (rec->type != RECORD_TYPE_EXPUNGE) | |
587 continue; | |
588 | |
589 if (rec->seq + before < first_seq) { | |
590 /* before our range */ | |
591 before++; | |
592 last_pos_seq--; | |
593 } else if (rec->seq <= last_pos_seq) { | |
594 /* within our range */ | |
595 last_pos_seq--; | |
596 | |
597 if (max_records-- == 0) { | |
598 /* log contains more data than it should | |
599 have - must be corrupted. */ | |
600 index_set_error(log->index, | |
601 "Modify log %s is corrupted", | |
602 log->filepath); | |
603 return NULL; | |
604 } | |
605 | |
606 *arr++ = rec->uid; | |
607 } | |
608 } | |
609 *arr = 0; | |
610 | |
611 /* sort the UID array, not including the terminating 0 */ | |
612 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
613 compare_uint); | |
614 | |
615 *expunges_before = before; | |
616 return expunges; | |
617 } | |
618 | |
619 const unsigned int * | |
620 mail_modifylog_uid_get_expunges(MailModifyLog *log, | |
621 unsigned int first_uid, | |
622 unsigned int last_uid) | |
623 { | |
624 /* pretty much copy&pasted from sequence code above .. | |
625 kind of annoying */ | |
626 ModifyLogRecord *rec, *end_rec; | |
627 unsigned int before, max_records, *arr, *expunges; | |
628 | |
629 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
630 | |
631 if (!mmap_update(log)) | |
632 return NULL; | |
633 | |
634 /* find the first expunged message that affects our range */ | |
635 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
636 log->synced_position); | |
637 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
638 log->mmap_length); | |
639 | |
640 while (rec < end_rec) { | |
641 if (rec->type == RECORD_TYPE_EXPUNGE && rec->uid <= last_uid) | |
642 break; | |
643 rec++; | |
644 } | |
645 | |
646 if (rec >= end_rec) { | |
647 /* none found */ | |
648 expunges = t_malloc(sizeof(unsigned int)); | |
649 *expunges = 0; | |
650 return expunges; | |
651 } | |
652 | |
653 /* allocate memory for the returned array. the file size - synced | |
654 position should be quite near the amount of memory we need, unless | |
655 there's lots of FLAGS_CHANGED records which is why there's the | |
656 second check to make sure it's not unneededly large. */ | |
657 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
658 sizeof(ModifyLogRecord); | |
659 if (max_records > last_uid - first_uid + 1) | |
660 max_records = last_uid - first_uid + 1; | |
661 | |
662 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
663 | |
664 before = 0; | |
665 while (rec < end_rec) { | |
666 if (rec->type == RECORD_TYPE_EXPUNGE && | |
667 rec->uid >= first_uid && rec->uid <= last_uid) { | |
668 /* within our range */ | |
669 if (max_records-- == 0) { | |
670 /* log contains more data than it should | |
671 have - must be corrupted. */ | |
672 index_set_error(log->index, | |
673 "Modify log %s is corrupted", | |
674 log->filepath); | |
675 return NULL; | |
676 } | |
677 *arr++ = rec->uid; | |
678 } | |
679 rec++; | |
680 } | |
681 *arr = 0; | |
682 | |
683 /* sort the UID array, not including the terminating 0 */ | |
684 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
685 compare_uint); | |
686 | |
687 return expunges; | |
688 } |