squat-uidlist.c revision 4fbd8de028e97cb14496fbcf74f3b878831274ae
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2006 Timo Sirainen */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define SQUAT_UIDLIST_FLUSH_THRESHOLD (1024*1024*31)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uint8_t uids_expunged; /* updated without locking */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void squat_uidlist_close(struct squat_uidlist *uidlist);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainensquat_uidlist_set_syscall_error(struct squat_uidlist *uidlist,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("%s failed with index search uidlist file %s: %m",
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainenstatic int squat_uidlist_check_header(struct squat_uidlist *uidlist,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* crashed before writing was finished */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (hdr->uidvalidity != uidlist->uidvalidity) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "uidlist: uidvalidity changed");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "uidlist: used_file_size too large");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int squat_uidlist_read_header(struct squat_uidlist *uidlist)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = pread_full(uidlist->fd, &uidlist->hdr, sizeof(uidlist->hdr), 0);
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "pread_full()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int squat_uidlist_map(struct squat_uidlist *uidlist)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct squat_uidlist_header *hdr = uidlist->mmap_base;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (hdr != NULL && hdr->used_file_size <= uidlist->mmap_size) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* everything is already mapped */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((ret = squat_uidlist_read_header(uidlist)) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "fstat()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (st.st_size <= (off_t)sizeof(uidlist->hdr))
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "munmap()");
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen mmap(NULL, uidlist->mmap_size, PROT_READ | PROT_WRITE,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "mmap()");
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen uidlist->const_mmap_base = uidlist->mmap_base;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen memcpy(&uidlist->hdr, uidlist->mmap_base, sizeof(uidlist->hdr));
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* the header is always read separately. everything between it
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen and the used_file_size doesn't change */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (squat_uidlist_check_header(uidlist, &uidlist->hdr, st.st_size) < 0)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen uidlist->first_new_list_idx = uidlist->hdr.used_file_size;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainenstatic int squat_uidlist_open(struct squat_uidlist *uidlist)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen uidlist->fd = open(uidlist->filepath, O_RDWR, 0600);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "open()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uidlist->file_cache = file_cache_new(uidlist->fd);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if ((ret = squat_uidlist_map(uidlist)) == 0) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "unlink()");
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenstatic int squat_uidlist_create(struct squat_uidlist *uidlist)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* we should get here only if normal file opening failed */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen uidlist->fd = open(uidlist->filepath, O_RDWR | O_CREAT | O_TRUNC, 0600);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "open()");
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen uidlist->file_cache = file_cache_new(uidlist->fd);
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainenstatic void squat_uidlist_close(struct squat_uidlist *uidlist)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "munmap()");
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "close()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainensquat_uidlist_init(struct squat_trie *trie, const char *path,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_alloconly_create(MEMPOOL_GROWING"squat uidlist node pool",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uidlist->tmp_buf = buffer_create_dynamic(default_pool, 16);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uidlist->list_buf = buffer_create_dynamic(default_pool, 256);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid squat_uidlist_deinit(struct squat_uidlist *uidlist)
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainenint squat_uidlist_refresh(struct squat_uidlist *uidlist)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen squat_uidlist_set_syscall_error(uidlist, "stat()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* no need to reopen, just remap */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* broken file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint squat_uidlist_get_last_uid(struct squat_uidlist *uidlist, uint32_t *uid_r)
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainenint squat_uidlist_add(struct squat_uidlist *uidlist, uint32_t *_uid_list_idx,
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen i_assert(uid > uidlist->hdr.uid_max || uid == uidlist->current_uid);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *_uid_list_idx = uid | UID_LIST_IDX_FLAG_SINGLE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (uid_list_idx < uidlist->first_new_list_idx) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* continue an existing list in the uidlist file */
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen } else if ((uid_list_idx & UID_LIST_IDX_FLAG_SINGLE) != 0) {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen uint32_t old_uid = uid_list_idx & ~UID_LIST_IDX_FLAG_SINGLE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* trying to add the same uid again */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* convert single UID to a list */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uidlist->node_pool_used += sizeof(struct uid_node);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen old_node = p_new(uidlist->node_pool, struct uid_node, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* update an in-memory list */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uint32_t arr_idx = uid_list_idx - uidlist->first_new_list_idx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (arr_idx >= array_count(&uidlist->lists)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "corrupted uidlist index (adding)");
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen node = array_idx_modifiable(&uidlist->lists, arr_idx);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* trying to add the same uid again */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uidlist->node_pool_used += sizeof(struct uid_node);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen old_node = p_new(uidlist->node_pool, struct uid_node, 1);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainensquat_uidlist_map_area(struct squat_uidlist *uidlist,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen ret = file_cache_read(uidlist->file_cache, offset, size);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen squat_uidlist_set_syscall_error(uidlist, "file_cache_read()");
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen file_cache_get_map(uidlist->file_cache, &uidlist->mmap_size);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainensquat_uidlist_map_list(struct squat_uidlist *uidlist, size_t offset,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (squat_uidlist_map_area(uidlist, offset, 128) < 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen end = uidlist->const_mmap_base + uidlist->mmap_size;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen data_offset = data - uidlist->const_mmap_base;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if (squat_uidlist_map_area(uidlist, data_offset, size) < 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *data_r = uidlist->const_mmap_base + data_offset;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainensquat_uidlist_copy_existing(struct squat_uidlist *uidlist, size_t offset,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen uint32_t *prev_uid_r, uint32_t *written_uid_r)
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen const uint8_t *data, *data_start, *end, *p = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (squat_uidlist_map_list(uidlist, offset, &data, &size) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen prev_uid = next_uid = _squat_trie_unpack_num(&data, end);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* prev_uid..next_uid */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try to increase this range */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* beginning a new uid/range */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen (end - (const uint8_t *)uidlist->const_mmap_base) - offset;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen buffer_append(uidlist->list_buf, data_start, p - data_start);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainensquat_uidlist_write_range(struct squat_uidlist *uidlist,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen uint32_t *prev_uid_r, uint32_t *written_uid_r,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen uint32_t prev_idx = POINTER_CAST_TO(node->prev, uint32_t);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* first UID */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if ((prev_idx & UID_NODE_PREV_FLAG_OLD) != 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (squat_uidlist_copy_existing(uidlist, prev_idx,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "corrupted continued uidlist index");
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (squat_uidlist_write_range(uidlist, node->prev,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* prev_uid contains the previous node's UID.
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen written_uid contains the last written UID. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* this node continue the range */
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen /* finishing range */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* range ends at prev_uid */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen _squat_trie_pack_num(buffer, node->uid - prev_uid - 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* no range */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int squat_uidlist_write_init(struct squat_uidlist *uidlist)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen uidlist->output = o_stream_create_file(uidlist->fd, default_pool,
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (uidlist->hdr.used_file_size < sizeof(uidlist->hdr)) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* creating a new file, write a dummy header */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenstatic int squat_uidlist_write_listbuf(struct squat_uidlist *uidlist,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* write size + buffer */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen _squat_trie_pack_num(uidlist->tmp_buf, uidlist->list_buf->used);
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen if (o_stream_send(output, uidlist->tmp_buf->data,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen o_stream_send(output, uidlist->list_buf->data,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenint squat_uidlist_finish_list(struct squat_uidlist *uidlist,
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if ((uid_list_idx & UID_LIST_IDX_FLAG_SINGLE) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* this is a single UID "list" */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (uid_list_idx < uidlist->first_new_list_idx) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* the list hasn't changed */
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen if (uid_list_idx >= array_count(&uidlist->lists)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "corrupted uidlist index (finishing)");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* write the uidlist into a buffer */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen node = array_idx(&uidlist->lists, uid_list_idx);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* new uidlist index is the offset in uidlist file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (squat_uidlist_write_listbuf(uidlist, uidlist->output) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void squat_uidlist_write_header(struct squat_uidlist *uidlist)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen uidlist->hdr.used_file_size = uidlist->output->offset;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen o_stream_send(uidlist->output, &uidlist->hdr, sizeof(uidlist->hdr));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenint squat_uidlist_flush(struct squat_uidlist *uidlist, uint32_t uid_validity)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenbool squat_uidlist_need_compress(struct squat_uidlist *uidlist,
return TRUE;
if (current_message_count == 0)
return TRUE;
return TRUE;
return FALSE;
bool update_disk)
if (update_disk) {
struct squat_uidlist_compress_ctx *
int fd;
return ctx;
return FALSE;
bool *all_expunged_r)
int ret;
ret = 0;
bool all_expunged;
&all_expunged);
if (ret < 0) {
if (ret == 0) {
int ret = 0;
if (ret == 0) {
if (ret < 0)
return ret;
unsigned int count;
if (count > 0) {
unsigned int *count_r)