0
|
1 /* Copyright (C) 2002 Timo Sirainen */
|
|
2
|
|
3 #include "lib.h"
|
|
4 #include "mmap-util.h"
|
|
5 #include "mail-index.h"
|
|
6 #include "mail-index-util.h"
|
|
7 #include "mail-modifylog.h"
|
|
8
|
|
9 #include <stdlib.h>
|
|
10 #include <fcntl.h>
|
|
11
|
|
12 /* Maximum size for modify log (isn't exact) */
|
|
13 #define MAX_MODIFYLOG_SIZE 10240
|
|
14
|
|
15 #define MODIFYLOG_FILE_POSITION(log, ptr) \
|
|
16 ((int) ((char *) (ptr) - (char *) (log)->mmap_base))
|
|
17
|
|
18 struct _MailModifyLog {
|
|
19 MailIndex *index;
|
|
20
|
|
21 int fd;
|
|
22 char *filepath;
|
|
23
|
|
24 void *mmap_base;
|
|
25 size_t mmap_length;
|
|
26
|
|
27 ModifyLogHeader *header;
|
|
28 size_t synced_position;
|
|
29 unsigned int synced_id, mmaped_id;
|
|
30
|
|
31 unsigned int modified:1;
|
|
32 unsigned int dirty_mmap:1;
|
|
33 unsigned int second_log:1;
|
|
34 };
|
|
35
|
|
36 static int file_lock(int fd, int wait_lock, int lock_type)
|
|
37 {
|
|
38 struct flock fl;
|
|
39
|
|
40 fl.l_type = lock_type;
|
|
41 fl.l_whence = SEEK_SET;
|
|
42 fl.l_start = 0;
|
|
43 fl.l_len = 0;
|
|
44
|
|
45 if (fcntl(fd, wait_lock ? F_SETLKW : F_SETLK, &fl) == -1) {
|
|
46 if (errno == EACCES)
|
|
47 return 0;
|
|
48 return -1;
|
|
49 }
|
|
50
|
|
51 return 1;
|
|
52 }
|
|
53
|
|
54 /* Returns 1 = ok, 0 = failed to get the lock, -1 = error */
|
|
55 static int mail_modifylog_try_lock(MailModifyLog *log, int lock_type)
|
|
56 {
|
|
57 int ret;
|
|
58
|
|
59 ret = file_lock(log->fd, FALSE, lock_type);
|
|
60 if (ret == -1) {
|
|
61 index_set_error(log->index, "fcntl() failed with file %s: %m",
|
|
62 log->filepath);
|
|
63 }
|
|
64
|
|
65 return ret;
|
|
66 }
|
|
67
|
|
68 static int mail_modifylog_wait_lock(MailModifyLog *log)
|
|
69 {
|
|
70 if (file_lock(log->fd, TRUE, F_RDLCK) < 1) {
|
|
71 index_set_error(log->index, "fcntl() failed with file %s: %m",
|
|
72 log->filepath);
|
|
73 return FALSE;
|
|
74 }
|
|
75
|
|
76 return TRUE;
|
|
77 }
|
|
78
|
|
79 /* returns 1 = yes, 0 = no, -1 = error */
|
|
80 static int mail_modifylog_have_other_users(MailModifyLog *log)
|
|
81 {
|
|
82 int ret;
|
|
83
|
|
84 /* try grabbing exclusive lock */
|
|
85 ret = mail_modifylog_try_lock(log, F_WRLCK);
|
|
86 if (ret == -1)
|
|
87 return -1;
|
|
88
|
|
89 /* revert back to shared lock */
|
|
90 switch (mail_modifylog_try_lock(log, F_WRLCK)) {
|
|
91 case 0:
|
|
92 /* shouldn't happen */
|
|
93 index_set_error(log->index, "fcntl(F_WRLCK -> F_RDLCK) "
|
|
94 "failed with file %s", log->filepath);
|
|
95 /* fall through */
|
|
96 case -1:
|
|
97 return -1;
|
|
98 }
|
|
99
|
|
100 return ret == 0 ? 1 : 0;
|
|
101 }
|
|
102
|
|
103 static int mmap_update(MailModifyLog *log)
|
|
104 {
|
|
105 unsigned int extra;
|
|
106
|
|
107 if (!log->dirty_mmap && log->mmaped_id == log->header->sync_id)
|
|
108 return TRUE;
|
|
109
|
|
110 if (log->mmap_base != NULL)
|
|
111 (void)munmap(log->mmap_base, log->mmap_length);
|
|
112
|
|
113 log->mmap_base = mmap_rw_file(log->fd, &log->mmap_length);
|
|
114 if (log->mmap_base == MAP_FAILED) {
|
|
115 log->mmap_base = NULL;
|
|
116 log->header = NULL;
|
|
117 index_set_error(log->index,
|
|
118 "modify log: mmap() failed with file %s: %m",
|
|
119 log->filepath);
|
|
120 return FALSE;
|
|
121 }
|
|
122
|
|
123 if (log->mmap_length < sizeof(ModifyLogHeader)) {
|
|
124 /* FIXME: we could do better.. */
|
|
125 (void)unlink(log->filepath);
|
|
126 i_assert(0);
|
|
127 }
|
|
128
|
|
129 extra = (log->mmap_length - sizeof(ModifyLogHeader)) %
|
|
130 sizeof(ModifyLogRecord);
|
|
131
|
|
132 if (extra != 0) {
|
|
133 /* partial write or corrupted -
|
|
134 truncate the file to valid length */
|
|
135 log->mmap_length -= extra;
|
|
136 (void)ftruncate(log->fd, (off_t) log->mmap_length);
|
|
137 }
|
|
138
|
|
139 log->dirty_mmap = FALSE;
|
|
140 log->header = log->mmap_base;
|
|
141 log->mmaped_id = log->header->sync_id;
|
|
142 return TRUE;
|
|
143 }
|
|
144
|
|
145 static MailModifyLog *mail_modifylog_new(MailIndex *index)
|
|
146 {
|
|
147 MailModifyLog *log;
|
|
148
|
|
149 log = i_new(MailModifyLog, 1);
|
|
150 log->fd = -1;
|
|
151 log->index = index;
|
|
152 log->dirty_mmap = TRUE;
|
|
153
|
|
154 index->modifylog = log;
|
|
155 return log;
|
|
156 }
|
|
157
|
|
158 static void mail_modifylog_close(MailModifyLog *log)
|
|
159 {
|
|
160 log->dirty_mmap = TRUE;
|
|
161
|
|
162 if (log->mmap_base != NULL) {
|
|
163 munmap(log->mmap_base, log->mmap_length);
|
|
164 log->mmap_base = NULL;
|
|
165 }
|
|
166
|
|
167 if (log->fd != -1) {
|
|
168 (void)close(log->fd);
|
|
169 log->fd = -1;
|
|
170 }
|
|
171
|
|
172 i_free(log->filepath);
|
|
173 }
|
|
174
|
|
175 static int mail_modifylog_init_fd(MailModifyLog *log, int fd,
|
|
176 const char *path)
|
|
177 {
|
|
178 ModifyLogHeader hdr;
|
|
179
|
|
180 /* write header */
|
|
181 memset(&hdr, 0, sizeof(hdr));
|
|
182 hdr.indexid = log->index->indexid;
|
|
183
|
|
184 if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
|
|
185 index_set_error(log->index, "write() failed for modify "
|
|
186 "log %s: %m", path);
|
|
187 return FALSE;
|
|
188 }
|
|
189
|
|
190 if (ftruncate(fd, sizeof(hdr)) == -1) {
|
|
191 index_set_error(log->index, "ftruncate() failed for modify "
|
|
192 "log %s: %m", path);
|
|
193 return FALSE;
|
|
194 }
|
|
195
|
|
196 return TRUE;
|
|
197 }
|
|
198
|
|
199 static int mail_modifylog_open_and_init_file(MailModifyLog *log,
|
|
200 const char *path)
|
|
201 {
|
|
202 int fd, ret;
|
|
203
|
|
204 fd = open(path, O_RDWR | O_CREAT, 0660);
|
|
205 if (fd == -1) {
|
|
206 index_set_error(log->index, "Error opening modify log "
|
|
207 "file %s: %m", path);
|
|
208 return FALSE;
|
|
209 }
|
|
210
|
|
211 ret = file_lock(fd, FALSE, F_WRLCK);
|
|
212 if (ret == -1) {
|
|
213 index_set_error(log->index, "Error locking modify log "
|
|
214 "file %s: %m", path);
|
|
215 }
|
|
216
|
|
217 if (ret == 1 && mail_modifylog_init_fd(log, fd, path)) {
|
|
218 mail_modifylog_close(log);
|
|
219
|
|
220 log->fd = fd;
|
|
221 log->filepath = i_strdup(path);
|
|
222 return TRUE;
|
|
223 }
|
|
224
|
|
225 (void)close(fd);
|
|
226 return FALSE;
|
|
227 }
|
|
228
|
|
229 int mail_modifylog_create(MailIndex *index)
|
|
230 {
|
|
231 MailModifyLog *log;
|
|
232 const char *path;
|
|
233
|
|
234 i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
|
|
235
|
|
236 log = mail_modifylog_new(index);
|
|
237
|
|
238 path = t_strconcat(log->index->filepath, ".log", NULL);
|
|
239 if (!mail_modifylog_open_and_init_file(log, path) ||
|
|
240 !mail_modifylog_wait_lock(log) ||
|
|
241 !mmap_update(log)) {
|
|
242 /* fatal failure */
|
|
243 mail_modifylog_free(log);
|
|
244 return FALSE;
|
|
245 }
|
|
246
|
|
247 log->synced_id = log->header->sync_id;
|
|
248 log->synced_position = log->mmap_length;
|
|
249 return TRUE;
|
|
250 }
|
|
251
|
|
252 /* Returns 1 = ok, 0 = full, -1 = error */
|
|
253 static int mail_modifylog_open_and_verify(MailModifyLog *log, const char *path)
|
|
254 {
|
|
255 ModifyLogHeader hdr;
|
|
256 int fd, ret;
|
|
257
|
|
258 fd = open(path, O_RDWR);
|
|
259 if (fd == -1) {
|
|
260 if (errno != ENOENT) {
|
|
261 index_set_error(log->index, "Can't open modify log "
|
|
262 "file %s: %m", path);
|
|
263 }
|
|
264 return -1;
|
|
265 }
|
|
266
|
|
267 ret = 1;
|
|
268 if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
|
|
269 index_set_error(log->index, "read() failed when for modify "
|
|
270 "log file %s: %m", path);
|
|
271 ret = -1;
|
|
272 }
|
|
273
|
|
274 if (ret != -1 && hdr.indexid != log->index->indexid) {
|
|
275 index_set_error(log->index, "IndexID mismatch for modify log "
|
|
276 "file %s", path);
|
|
277 ret = -1;
|
|
278 }
|
|
279
|
|
280 if (ret != -1 && hdr.sync_id == SYNC_ID_FULL) {
|
|
281 /* full */
|
|
282 ret = 0;
|
|
283 }
|
|
284
|
|
285 if (ret == 1) {
|
|
286 log->fd = fd;
|
|
287 log->filepath = i_strdup(path);
|
|
288 } else {
|
|
289 (void)close(fd);
|
|
290 }
|
|
291
|
|
292 return ret;
|
|
293 }
|
|
294
|
|
295 static int mail_modifylog_find_or_create(MailModifyLog *log)
|
|
296 {
|
|
297 const char *path1, *path2;
|
|
298 int i;
|
|
299
|
|
300 for (i = 0; i < 2; i++) {
|
|
301 /* first try <index>.log */
|
|
302 path1 = t_strconcat(log->index->filepath, ".log", NULL);
|
|
303 if (mail_modifylog_open_and_verify(log, path1) == 1)
|
|
304 return TRUE;
|
|
305
|
|
306 /* then <index>.log.2 */
|
|
307 path2 = t_strconcat(log->index->filepath, ".log.2", NULL);
|
|
308 if (mail_modifylog_open_and_verify(log, path2) == 1)
|
|
309 return TRUE;
|
|
310
|
|
311 /* try creating/reusing them */
|
|
312 if (mail_modifylog_open_and_init_file(log, path1))
|
|
313 return TRUE;
|
|
314
|
|
315 if (mail_modifylog_open_and_init_file(log, path2))
|
|
316 return TRUE;
|
|
317
|
|
318 /* maybe the file was just switched, check the logs again */
|
|
319 }
|
|
320
|
|
321 index_set_error(log->index, "We could neither use nor create "
|
|
322 "the modify log for index %s", log->index->filepath);
|
|
323 return FALSE;
|
|
324 }
|
|
325
|
|
326 int mail_modifylog_open_or_create(MailIndex *index)
|
|
327 {
|
|
328 MailModifyLog *log;
|
|
329
|
|
330 log = mail_modifylog_new(index);
|
|
331
|
|
332 if (!mail_modifylog_find_or_create(log) ||
|
|
333 !mail_modifylog_wait_lock(log) ||
|
|
334 !mmap_update(log)) {
|
|
335 /* fatal failure */
|
|
336 mail_modifylog_free(log);
|
|
337 return FALSE;
|
|
338 }
|
|
339
|
|
340 log->synced_id = log->header->sync_id;
|
|
341 log->synced_position = log->mmap_length;
|
|
342 return TRUE;
|
|
343 }
|
|
344
|
|
345 void mail_modifylog_free(MailModifyLog *log)
|
|
346 {
|
|
347 log->index->modifylog = NULL;
|
|
348
|
|
349 mail_modifylog_close(log);
|
|
350 i_free(log);
|
|
351 }
|
|
352
|
|
353 int mail_modifylog_sync_file(MailModifyLog *log)
|
|
354 {
|
|
355 if (!log->modified)
|
|
356 return TRUE;
|
|
357
|
|
358 if (log->mmap_base != NULL) {
|
|
359 if (msync(log->mmap_base, log->mmap_length, MS_SYNC) == -1) {
|
|
360 index_set_error(log->index, "msync() failed for %s: %m",
|
|
361 log->filepath);
|
|
362 return FALSE;
|
|
363 }
|
|
364 }
|
|
365
|
|
366 if (fsync(log->fd) == -1) {
|
|
367 index_set_error(log->index, "fsync() failed for %s: %m",
|
|
368 log->filepath);
|
|
369 return FALSE;
|
|
370 }
|
|
371
|
|
372 log->modified = FALSE;
|
|
373 return TRUE;
|
|
374 }
|
|
375
|
|
376 static int mail_modifylog_append(MailModifyLog *log, ModifyLogRecord *rec,
|
|
377 int external_change)
|
|
378 {
|
|
379 i_assert(log->index->lock_type == MAIL_LOCK_EXCLUSIVE);
|
|
380 i_assert(rec->seq != 0);
|
|
381 i_assert(rec->uid != 0);
|
|
382
|
|
383 if (!external_change) {
|
|
384 switch (mail_modifylog_have_other_users(log)) {
|
|
385 case 0:
|
|
386 /* we're the only one having this log open,
|
|
387 no need for modify log. */
|
|
388 return TRUE;
|
|
389 case -1:
|
|
390 return FALSE;
|
|
391 }
|
|
392 }
|
|
393
|
|
394 if (lseek(log->fd, 0, SEEK_END) == (off_t)-1) {
|
|
395 index_set_error(log->index, "lseek() failed with file %s: %m",
|
|
396 log->filepath);
|
|
397 return FALSE;
|
|
398 }
|
|
399
|
|
400 if (write(log->fd, rec, sizeof(ModifyLogRecord)) !=
|
|
401 sizeof(ModifyLogRecord)) {
|
|
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 }
|