Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-index/mail-modifylog.c @ 90:ccb155c2c93f HEAD
mail_modifylog_mark_synced() didn't update mmap
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 29 Aug 2002 01:39:13 +0300 |
parents | d493b9cc265e |
children | 91e5141f994c |
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) { | |
47 if (errno == EACCES) | |
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 */ | |
91 switch (mail_modifylog_try_lock(log, F_WRLCK)) { | |
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; | |
279 } | |
280 | |
281 if (ret != -1 && hdr.sync_id == SYNC_ID_FULL) { | |
282 /* full */ | |
283 ret = 0; | |
284 } | |
285 | |
286 if (ret == 1) { | |
287 log->fd = fd; | |
288 log->filepath = i_strdup(path); | |
289 } else { | |
290 (void)close(fd); | |
291 } | |
292 | |
293 return ret; | |
294 } | |
295 | |
296 static int mail_modifylog_find_or_create(MailModifyLog *log) | |
297 { | |
298 const char *path1, *path2; | |
299 int i; | |
300 | |
301 for (i = 0; i < 2; i++) { | |
302 /* first try <index>.log */ | |
303 path1 = t_strconcat(log->index->filepath, ".log", NULL); | |
304 if (mail_modifylog_open_and_verify(log, path1) == 1) | |
305 return TRUE; | |
306 | |
307 /* then <index>.log.2 */ | |
308 path2 = t_strconcat(log->index->filepath, ".log.2", NULL); | |
309 if (mail_modifylog_open_and_verify(log, path2) == 1) | |
310 return TRUE; | |
311 | |
312 /* try creating/reusing them */ | |
313 if (mail_modifylog_open_and_init_file(log, path1)) | |
314 return TRUE; | |
315 | |
316 if (mail_modifylog_open_and_init_file(log, path2)) | |
317 return TRUE; | |
318 | |
319 /* maybe the file was just switched, check the logs again */ | |
320 } | |
321 | |
322 index_set_error(log->index, "We could neither use nor create " | |
323 "the modify log for index %s", log->index->filepath); | |
324 return FALSE; | |
325 } | |
326 | |
327 int mail_modifylog_open_or_create(MailIndex *index) | |
328 { | |
329 MailModifyLog *log; | |
330 | |
331 log = mail_modifylog_new(index); | |
332 | |
333 if (!mail_modifylog_find_or_create(log) || | |
334 !mail_modifylog_wait_lock(log) || | |
335 !mmap_update(log)) { | |
336 /* fatal failure */ | |
337 mail_modifylog_free(log); | |
338 return FALSE; | |
339 } | |
340 | |
341 log->synced_id = log->header->sync_id; | |
342 log->synced_position = log->mmap_length; | |
343 return TRUE; | |
344 } | |
345 | |
346 void mail_modifylog_free(MailModifyLog *log) | |
347 { | |
348 log->index->modifylog = NULL; | |
349 | |
350 mail_modifylog_close(log); | |
351 i_free(log); | |
352 } | |
353 | |
354 int mail_modifylog_sync_file(MailModifyLog *log) | |
355 { | |
356 if (!log->modified) | |
357 return TRUE; | |
358 | |
359 if (log->mmap_base != NULL) { | |
360 if (msync(log->mmap_base, log->mmap_length, MS_SYNC) == -1) { | |
361 index_set_error(log->index, "msync() failed for %s: %m", | |
362 log->filepath); | |
363 return FALSE; | |
364 } | |
365 } | |
366 | |
367 if (fsync(log->fd) == -1) { | |
368 index_set_error(log->index, "fsync() failed for %s: %m", | |
369 log->filepath); | |
370 return FALSE; | |
371 } | |
372 | |
373 log->modified = FALSE; | |
374 return TRUE; | |
375 } | |
376 | |
377 static int mail_modifylog_append(MailModifyLog *log, ModifyLogRecord *rec, | |
378 int external_change) | |
379 { | |
380 i_assert(log->index->lock_type == MAIL_LOCK_EXCLUSIVE); | |
381 i_assert(rec->seq != 0); | |
382 i_assert(rec->uid != 0); | |
383 | |
384 if (!external_change) { | |
385 switch (mail_modifylog_have_other_users(log)) { | |
386 case 0: | |
387 /* we're the only one having this log open, | |
388 no need for modify log. */ | |
389 return TRUE; | |
390 case -1: | |
391 return FALSE; | |
392 } | |
393 } | |
394 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
395 if (lseek(log->fd, 0, SEEK_END) == -1) { |
0 | 396 index_set_error(log->index, "lseek() failed with file %s: %m", |
397 log->filepath); | |
398 return FALSE; | |
399 } | |
400 | |
29
e9375147c0cb
Added write_full() which is a simple wrapper around write() meant for
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
401 if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0) { |
0 | 402 index_set_error(log->index, "Error appending to file %s: %m", |
403 log->filepath); | |
404 return FALSE; | |
405 } | |
406 | |
407 log->header->sync_id++; | |
408 log->modified = TRUE; | |
409 log->dirty_mmap = TRUE; | |
410 | |
411 if (!external_change) { | |
412 log->synced_id = log->header->sync_id; | |
413 log->synced_position += sizeof(ModifyLogRecord); | |
414 } | |
415 return TRUE; | |
416 } | |
417 | |
418 int mail_modifylog_add_expunge(MailModifyLog *log, unsigned int seq, | |
419 unsigned int uid, int external_change) | |
420 { | |
421 ModifyLogRecord rec; | |
422 | |
423 /* expunges must not be added when log isn't synced */ | |
424 i_assert(external_change || log->synced_id == log->header->sync_id); | |
425 | |
426 rec.type = RECORD_TYPE_EXPUNGE; | |
427 rec.seq = seq; | |
428 rec.uid = uid; | |
429 return mail_modifylog_append(log, &rec, external_change); | |
430 } | |
431 | |
432 int mail_modifylog_add_flags(MailModifyLog *log, unsigned int seq, | |
433 unsigned int uid, int external_change) | |
434 { | |
435 ModifyLogRecord rec; | |
436 | |
437 rec.type = RECORD_TYPE_FLAGS_CHANGED; | |
438 rec.seq = seq; | |
439 rec.uid = uid; | |
440 return mail_modifylog_append(log, &rec, external_change); | |
441 } | |
442 | |
443 ModifyLogRecord *mail_modifylog_get_nonsynced(MailModifyLog *log, | |
444 unsigned int *count) | |
445 { | |
446 ModifyLogRecord *rec, *end_rec; | |
447 | |
448 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
449 | |
450 *count = 0; | |
451 if (!mmap_update(log)) | |
452 return NULL; | |
453 | |
454 i_assert(log->synced_position <= log->mmap_length); | |
455 i_assert(log->synced_position >= sizeof(ModifyLogHeader)); | |
456 | |
457 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
458 log->synced_position); | |
459 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
460 log->mmap_length); | |
461 *count = (unsigned int) (end_rec - rec); | |
462 return rec; | |
463 } | |
464 | |
465 static int mail_modifylog_switch_file(MailModifyLog *log) | |
466 { | |
467 MailIndex *index = log->index; | |
468 | |
469 mail_modifylog_free(log); | |
470 return mail_modifylog_open_or_create(index); | |
471 } | |
472 | |
473 static void mail_modifylog_try_switch_file(MailModifyLog *log) | |
474 { | |
475 const char *path; | |
476 | |
477 path = t_strconcat(log->index->filepath, | |
478 log->second_log ? ".log" : ".log.2", NULL); | |
479 | |
480 if (mail_modifylog_open_and_init_file(log, path)) | |
481 log->header->sync_id = SYNC_ID_FULL; | |
482 } | |
483 | |
484 int mail_modifylog_mark_synced(MailModifyLog *log) | |
485 { | |
486 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
487 | |
90
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
488 if (!mmap_update(log)) |
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
489 return FALSE; |
ccb155c2c93f
mail_modifylog_mark_synced() didn't update mmap
Timo Sirainen <tss@iki.fi>
parents:
50
diff
changeset
|
490 |
0 | 491 if (log->header->sync_id == SYNC_ID_FULL) { |
492 /* log file is full, switch to next one */ | |
493 return mail_modifylog_switch_file(log); | |
494 } | |
495 | |
496 if (log->synced_id == log->header->sync_id) { | |
497 /* we are already synced */ | |
498 return TRUE; | |
499 } | |
500 | |
501 log->synced_id = log->header->sync_id; | |
502 log->synced_position = log->mmap_length; | |
503 | |
504 log->modified = TRUE; | |
505 | |
506 if (log->mmap_length > MAX_MODIFYLOG_SIZE) { | |
507 /* if the other file isn't locked, switch to it */ | |
508 mail_modifylog_try_switch_file(log); | |
509 return TRUE; | |
510 } | |
511 | |
512 return TRUE; | |
513 } | |
514 | |
515 static int compare_uint(const void *p1, const void *p2) | |
516 { | |
517 const unsigned int *u1 = p1; | |
518 const unsigned int *u2 = p2; | |
519 | |
520 return *u1 < *u2 ? -1 : *u1 > *u2 ? 1 : 0; | |
521 } | |
522 | |
523 const unsigned int * | |
524 mail_modifylog_seq_get_expunges(MailModifyLog *log, | |
525 unsigned int first_seq, | |
526 unsigned int last_seq, | |
527 unsigned int *expunges_before) | |
528 { | |
529 ModifyLogRecord *rec, *end_rec; | |
530 unsigned int last_pos_seq, before, max_records, *arr, *expunges; | |
531 | |
532 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
533 | |
534 *expunges_before = 0; | |
535 | |
536 if (!mmap_update(log)) | |
537 return NULL; | |
538 | |
539 /* find the first expunged message that affects our range */ | |
540 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
541 log->synced_position); | |
542 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
543 log->mmap_length); | |
544 | |
545 while (rec < end_rec) { | |
546 if (rec->type == RECORD_TYPE_EXPUNGE && rec->seq <= last_seq) | |
547 break; | |
548 rec++; | |
549 } | |
550 | |
551 if (rec >= end_rec) { | |
552 /* none found */ | |
553 expunges = t_malloc(sizeof(unsigned int)); | |
554 *expunges = 0; | |
555 return expunges; | |
556 } | |
557 | |
558 /* allocate memory for the returned array. the file size - synced | |
559 position should be quite near the amount of memory we need, unless | |
560 there's lots of FLAGS_CHANGED records which is why there's the | |
561 second check to make sure it's not unneededly large. */ | |
562 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
563 sizeof(ModifyLogRecord); | |
564 if (max_records > last_seq - first_seq + 1) | |
565 max_records = last_seq - first_seq + 1; | |
566 | |
567 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
568 | |
569 /* last_pos_seq is updated all the time to contain the last_seq | |
570 comparable to current record's seq. number */ | |
571 last_pos_seq = last_seq; | |
572 | |
573 before = 0; | |
574 for (; rec < end_rec; rec++) { | |
575 if (rec->type != RECORD_TYPE_EXPUNGE) | |
576 continue; | |
577 | |
578 if (rec->seq + before < first_seq) { | |
579 /* before our range */ | |
580 before++; | |
581 last_pos_seq--; | |
582 } else if (rec->seq <= last_pos_seq) { | |
583 /* within our range */ | |
584 last_pos_seq--; | |
585 | |
586 if (max_records-- == 0) { | |
587 /* log contains more data than it should | |
588 have - must be corrupted. */ | |
589 index_set_error(log->index, | |
590 "Modify log %s is corrupted", | |
591 log->filepath); | |
592 return NULL; | |
593 } | |
594 | |
595 *arr++ = rec->uid; | |
596 } | |
597 } | |
598 *arr = 0; | |
599 | |
600 /* sort the UID array, not including the terminating 0 */ | |
601 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
602 compare_uint); | |
603 | |
604 *expunges_before = before; | |
605 return expunges; | |
606 } | |
607 | |
608 const unsigned int * | |
609 mail_modifylog_uid_get_expunges(MailModifyLog *log, | |
610 unsigned int first_uid, | |
611 unsigned int last_uid) | |
612 { | |
613 /* pretty much copy&pasted from sequence code above .. | |
614 kind of annoying */ | |
615 ModifyLogRecord *rec, *end_rec; | |
616 unsigned int before, max_records, *arr, *expunges; | |
617 | |
618 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
619 | |
620 if (!mmap_update(log)) | |
621 return NULL; | |
622 | |
623 /* find the first expunged message that affects our range */ | |
624 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
625 log->synced_position); | |
626 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
627 log->mmap_length); | |
628 | |
629 while (rec < end_rec) { | |
630 if (rec->type == RECORD_TYPE_EXPUNGE && rec->uid <= last_uid) | |
631 break; | |
632 rec++; | |
633 } | |
634 | |
635 if (rec >= end_rec) { | |
636 /* none found */ | |
637 expunges = t_malloc(sizeof(unsigned int)); | |
638 *expunges = 0; | |
639 return expunges; | |
640 } | |
641 | |
642 /* allocate memory for the returned array. the file size - synced | |
643 position should be quite near the amount of memory we need, unless | |
644 there's lots of FLAGS_CHANGED records which is why there's the | |
645 second check to make sure it's not unneededly large. */ | |
646 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
647 sizeof(ModifyLogRecord); | |
648 if (max_records > last_uid - first_uid + 1) | |
649 max_records = last_uid - first_uid + 1; | |
650 | |
651 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
652 | |
653 before = 0; | |
654 while (rec < end_rec) { | |
655 if (rec->type == RECORD_TYPE_EXPUNGE && | |
656 rec->uid >= first_uid && rec->uid <= last_uid) { | |
657 /* within our range */ | |
658 if (max_records-- == 0) { | |
659 /* log contains more data than it should | |
660 have - must be corrupted. */ | |
661 index_set_error(log->index, | |
662 "Modify log %s is corrupted", | |
663 log->filepath); | |
664 return NULL; | |
665 } | |
666 *arr++ = rec->uid; | |
667 } | |
668 rec++; | |
669 } | |
670 *arr = 0; | |
671 | |
672 /* sort the UID array, not including the terminating 0 */ | |
673 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
674 compare_uint); | |
675 | |
676 return expunges; | |
677 } |