Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-index/mail-modifylog.c @ 50:d493b9cc265e HEAD
Introduced uoff_t which is the unsigned-equilevant of off_t. This was needed
to be able to handle off_t overflows properly. Also changed a few unsigned
int fields into uoff_t so we should now support >2G mails if uoff_t is
64bit. Also fixed several potential integer overflows.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 27 Aug 2002 22:16:54 +0300 |
parents | e9375147c0cb |
children | ccb155c2c93f |
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 | |
488 if (log->header->sync_id == SYNC_ID_FULL) { | |
489 /* log file is full, switch to next one */ | |
490 return mail_modifylog_switch_file(log); | |
491 } | |
492 | |
493 if (log->synced_id == log->header->sync_id) { | |
494 /* we are already synced */ | |
495 return TRUE; | |
496 } | |
497 | |
498 log->synced_id = log->header->sync_id; | |
499 log->synced_position = log->mmap_length; | |
500 | |
501 log->modified = TRUE; | |
502 | |
503 if (log->mmap_length > MAX_MODIFYLOG_SIZE) { | |
504 /* if the other file isn't locked, switch to it */ | |
505 mail_modifylog_try_switch_file(log); | |
506 return TRUE; | |
507 } | |
508 | |
509 return TRUE; | |
510 } | |
511 | |
512 static int compare_uint(const void *p1, const void *p2) | |
513 { | |
514 const unsigned int *u1 = p1; | |
515 const unsigned int *u2 = p2; | |
516 | |
517 return *u1 < *u2 ? -1 : *u1 > *u2 ? 1 : 0; | |
518 } | |
519 | |
520 const unsigned int * | |
521 mail_modifylog_seq_get_expunges(MailModifyLog *log, | |
522 unsigned int first_seq, | |
523 unsigned int last_seq, | |
524 unsigned int *expunges_before) | |
525 { | |
526 ModifyLogRecord *rec, *end_rec; | |
527 unsigned int last_pos_seq, before, max_records, *arr, *expunges; | |
528 | |
529 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
530 | |
531 *expunges_before = 0; | |
532 | |
533 if (!mmap_update(log)) | |
534 return NULL; | |
535 | |
536 /* find the first expunged message that affects our range */ | |
537 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
538 log->synced_position); | |
539 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
540 log->mmap_length); | |
541 | |
542 while (rec < end_rec) { | |
543 if (rec->type == RECORD_TYPE_EXPUNGE && rec->seq <= last_seq) | |
544 break; | |
545 rec++; | |
546 } | |
547 | |
548 if (rec >= end_rec) { | |
549 /* none found */ | |
550 expunges = t_malloc(sizeof(unsigned int)); | |
551 *expunges = 0; | |
552 return expunges; | |
553 } | |
554 | |
555 /* allocate memory for the returned array. the file size - synced | |
556 position should be quite near the amount of memory we need, unless | |
557 there's lots of FLAGS_CHANGED records which is why there's the | |
558 second check to make sure it's not unneededly large. */ | |
559 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
560 sizeof(ModifyLogRecord); | |
561 if (max_records > last_seq - first_seq + 1) | |
562 max_records = last_seq - first_seq + 1; | |
563 | |
564 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
565 | |
566 /* last_pos_seq is updated all the time to contain the last_seq | |
567 comparable to current record's seq. number */ | |
568 last_pos_seq = last_seq; | |
569 | |
570 before = 0; | |
571 for (; rec < end_rec; rec++) { | |
572 if (rec->type != RECORD_TYPE_EXPUNGE) | |
573 continue; | |
574 | |
575 if (rec->seq + before < first_seq) { | |
576 /* before our range */ | |
577 before++; | |
578 last_pos_seq--; | |
579 } else if (rec->seq <= last_pos_seq) { | |
580 /* within our range */ | |
581 last_pos_seq--; | |
582 | |
583 if (max_records-- == 0) { | |
584 /* log contains more data than it should | |
585 have - must be corrupted. */ | |
586 index_set_error(log->index, | |
587 "Modify log %s is corrupted", | |
588 log->filepath); | |
589 return NULL; | |
590 } | |
591 | |
592 *arr++ = rec->uid; | |
593 } | |
594 } | |
595 *arr = 0; | |
596 | |
597 /* sort the UID array, not including the terminating 0 */ | |
598 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
599 compare_uint); | |
600 | |
601 *expunges_before = before; | |
602 return expunges; | |
603 } | |
604 | |
605 const unsigned int * | |
606 mail_modifylog_uid_get_expunges(MailModifyLog *log, | |
607 unsigned int first_uid, | |
608 unsigned int last_uid) | |
609 { | |
610 /* pretty much copy&pasted from sequence code above .. | |
611 kind of annoying */ | |
612 ModifyLogRecord *rec, *end_rec; | |
613 unsigned int before, max_records, *arr, *expunges; | |
614 | |
615 i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK); | |
616 | |
617 if (!mmap_update(log)) | |
618 return NULL; | |
619 | |
620 /* find the first expunged message that affects our range */ | |
621 rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
622 log->synced_position); | |
623 end_rec = (ModifyLogRecord *) ((char *) log->mmap_base + | |
624 log->mmap_length); | |
625 | |
626 while (rec < end_rec) { | |
627 if (rec->type == RECORD_TYPE_EXPUNGE && rec->uid <= last_uid) | |
628 break; | |
629 rec++; | |
630 } | |
631 | |
632 if (rec >= end_rec) { | |
633 /* none found */ | |
634 expunges = t_malloc(sizeof(unsigned int)); | |
635 *expunges = 0; | |
636 return expunges; | |
637 } | |
638 | |
639 /* allocate memory for the returned array. the file size - synced | |
640 position should be quite near the amount of memory we need, unless | |
641 there's lots of FLAGS_CHANGED records which is why there's the | |
642 second check to make sure it's not unneededly large. */ | |
643 max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) / | |
644 sizeof(ModifyLogRecord); | |
645 if (max_records > last_uid - first_uid + 1) | |
646 max_records = last_uid - first_uid + 1; | |
647 | |
648 expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int)); | |
649 | |
650 before = 0; | |
651 while (rec < end_rec) { | |
652 if (rec->type == RECORD_TYPE_EXPUNGE && | |
653 rec->uid >= first_uid && rec->uid <= last_uid) { | |
654 /* within our range */ | |
655 if (max_records-- == 0) { | |
656 /* log contains more data than it should | |
657 have - must be corrupted. */ | |
658 index_set_error(log->index, | |
659 "Modify log %s is corrupted", | |
660 log->filepath); | |
661 return NULL; | |
662 } | |
663 *arr++ = rec->uid; | |
664 } | |
665 rec++; | |
666 } | |
667 *arr = 0; | |
668 | |
669 /* sort the UID array, not including the terminating 0 */ | |
670 qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int), | |
671 compare_uint); | |
672 | |
673 return expunges; | |
674 } |