Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-index/mail-modifylog.c @ 199:cf1279829ad1 HEAD
custom flag & copy crash fixes.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 09 Sep 2002 14:55:27 +0300 |
parents | ff05b320482c |
children | ed0d5b17c7a4 |
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); | |
199 | 127 i_panic("modify log corrupted"); |
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 } |