squat-uidlist.c revision 78b806afb68cf1fed9dda6b45cbf7f8f2e08260d
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* set = points to uidlist index number, unset = points to uidlist offset */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UID_LIST_POINTER_MASK_LIST_IDX 0x80000000
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER 2
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct squat_uidlist_build_context *build_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_TYPE(uint32_t) new_block_offsets, new_block_end_indexes;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t list_sizes[UIDLIST_BLOCK_LIST_COUNT];
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void squat_uidlist_close(struct squat_uidlist *uidlist);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainenvoid squat_uidlist_delete(struct squat_uidlist *uidlist)
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void squat_uidlist_set_corrupted(struct squat_uidlist *uidlist,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen i_error("Corrupted squat uidlist file %s: %s", uidlist->path, reason);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainenuidlist_write_array(struct ostream *output, const uint32_t *uid_list,
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen unsigned int uid_count, uint32_t packed_flags,
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen uint32_t offset, bool write_size, uint32_t *size_r)
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen uint8_t *uidbuf, *bufp, sizebuf[SQUAT_PACK_MAX_SIZE], *sizebufp;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen uint8_t listbuf[SQUAT_PACK_MAX_SIZE], *listbufp = listbuf;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen uint32_t uid, uid2, prev, base_uid, size_value;
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen if ((packed_flags & UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER) != 0)
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen /* @UNSAFE */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen base_uid = uid_list[0] & ~UID_LIST_MASK_RANGE;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen datastack = uid_count < 1024*8/SQUAT_PACK_MAX_SIZE;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen uidbuf = t_malloc_no0(SQUAT_PACK_MAX_SIZE * uid_count);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen uidbuf = i_malloc(SQUAT_PACK_MAX_SIZE * uid_count);
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen bitmask_len = (uid_list[uid_count-1] - base_uid + 7) / 8 +
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen i_assert(bitmask_len < SQUAT_PACK_MAX_SIZE*uid_count);
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainen memset(bufp, 0, bitmask_len - (bufp - uidbuf));
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainen if ((uid_list[0] & UID_LIST_MASK_RANGE) == 0) {
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen for (; i < uid_count; i++) {
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen i_assert((uid & ~UID_LIST_MASK_RANGE) >= base_uid);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* first byte */
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen /* middle bytes */
8830fab191cab8440281eb641dfdd93974b2933bTimo Sirainen /* last byte */
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen for (i = 0; i < uid_count; i++) {
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen if (unlikely((uid & ~UID_LIST_MASK_RANGE) < prev)) {
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen squat_pack_num(&bufp, uid_list[i+1] - uid - 1);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen o_stream_nsend(output, sizebuf, sizebufp - sizebuf);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_nsend(output, listbuf, listbufp - listbuf);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuidlist_write(struct ostream *output, const struct uidlist_list *list,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* continued UID list */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen packed_flags |= UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((uid_list[0] & UID_LIST_POINTER_MASK_LIST_IDX) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen offset = ((uid_list[0] & ~UID_LIST_POINTER_MASK_LIST_IDX) << 1) | 1;
c1d45cada20777e1973579d40d0ebe43f89bb053Timo Sirainen } else if (unlikely(output->offset <= uid_list[0])) {
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen ret = uidlist_write_array(output, uid_list, uid_count,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void squat_uidlist_map_blocks_set_pointers(struct squat_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen base = CONST_PTR_OFFSET(uidlist->data, uidlist->hdr.block_list_offset +
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen end_index_size = uidlist->cur_block_count * sizeof(uint32_t);
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen end_size = end_index_size + uidlist->cur_block_count * sizeof(uint32_t);
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainenstatic int uidlist_file_cache_read(struct squat_uidlist *uidlist,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen if (file_cache_read(uidlist->file_cache, offset, size) < 0) {
d6f50f100ce17fa4b3a89e9567a5ff993b38b872Timo Sirainen i_error("read(%s) failed: %m", uidlist->path);
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen uidlist->data = file_cache_get_map(uidlist->file_cache,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen squat_uidlist_map_blocks_set_pointers(uidlist);
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainenstatic int squat_uidlist_map_blocks(struct squat_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct squat_uidlist_file_header *hdr = &uidlist->hdr;
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen uint32_t block_count, blocks_offset, blocks_size, i, verify_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* empty file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* get number of blocks */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen if (uidlist_file_cache_read(uidlist, hdr->block_list_offset,
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen blocks_offset = hdr->block_list_offset + sizeof(block_count);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen squat_uidlist_set_corrupted(uidlist, "block list outside file");
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen base = CONST_PTR_OFFSET(uidlist->data, hdr->block_list_offset);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen memcpy(&block_count, base, sizeof(block_count));
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* map the blocks */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen blocks_size = block_count * sizeof(uint32_t)*2;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (uidlist_file_cache_read(uidlist, blocks_offset, blocks_size) < 0)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen if (blocks_offset + blocks_size > uidlist->data_size) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen squat_uidlist_set_corrupted(uidlist, "block list outside file");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen squat_uidlist_map_blocks_set_pointers(uidlist);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen i_assert(uidlist->cur_block_end_indexes != NULL);
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen /* verify just a couple of the end indexes to make sure they
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen look correct */
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen if (unlikely(uidlist->cur_block_end_indexes[i-1] >=
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "block list corrupted");
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainenstatic int squat_uidlist_map_header(struct squat_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* still being built */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->hdr.indexid != uidlist->trie->hdr.indexid) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* see if trie was recreated */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->hdr.indexid != uidlist->trie->hdr.indexid) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen squat_uidlist_set_corrupted(uidlist, "wrong indexid");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->hdr.used_file_size < sizeof(uidlist->hdr) ||
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen (uidlist->hdr.used_file_size > uidlist->mmap_size &&
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen squat_uidlist_set_corrupted(uidlist, "broken used_file_size");
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainenstatic void squat_uidlist_unmap(struct squat_uidlist *uidlist)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen i_error("munmap(%s) failed: %m", uidlist->path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int squat_uidlist_mmap(struct squat_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("fstat(%s) failed: %m", uidlist->path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (st.st_size < (off_t)sizeof(uidlist->hdr)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen squat_uidlist_set_corrupted(uidlist, "File too small");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->mmap_base = mmap(NULL, uidlist->mmap_size,
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen i_error("mmap(%s) failed: %m", uidlist->path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int squat_uidlist_map(struct squat_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct squat_uidlist_file_header *mmap_hdr = uidlist->mmap_base;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->hdr.block_list_offset == mmap_hdr->block_list_offset) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* file hasn't changed */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if ((uidlist->trie->flags & SQUAT_INDEX_FLAG_MMAP_DISABLE) == 0) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen uidlist->mmap_size < mmap_hdr->used_file_size) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we want to update blocks mapping, but using the header
if (ret < 0) {
return uidlist;
int ret;
if (ret == 0) {
if (ret < 0)
if (ret == 0)
if (ret < 0)
int ret;
if (ret == 0) {
int ret;
if (ret == 0 &&
if (ret < 0) {
bool write_old_blocks)
if (align != 0) {
if (count == 0)
for (j = 0; j < max; j++) {
for (j = 0; j < max; j++) {
bool compress,
const char *temp_path;
int fd;
if (!compress) {
int ret;
T_BEGIN {
} T_END;
if (ret < 0)
unsigned int i, count;
if (count == 0)
ret = 0;
for (i = 0; i < count; i++) {
return ret;
for (i = 0; i < count; i++) {
return ret;
bool cancel)
const char *temp_path;
ret = 0;
if (ret > 0) {
FALSE);
if (ret <= 0)
static struct uidlist_list *
return list;
uint32_t *p;
return uid_list_idx;
return uid_list_idx;
if (uid_list_idx == 0) {
idx = 0;
return uid_list_idx;
return uid_list_idx;
return uid_list_idx;
*p |= UID_LIST_MASK_RANGE;
return uid_list_idx;
*p = uid;
return uid_list_idx;
unsigned int count;
if (count == 0) {
UID_LIST_MASK_RANGE) != 0) {
unsigned int count;
if (count == 0) {
UID_LIST_MASK_RANGE) != 0) {
unsigned int i, j, count;
if (num != 0)
SQUAT_PACK_MAX_SIZE) < 0)
0, uids) < 0)
next_uid = 0;
for (i = 0; i < size; i++) {
base_uid);
if (p == end)
unsigned int idx;
idx++;
max_map_size) < 0)
unsigned int mask;
return idx;
i_unreached();
unsigned int i, count;
int ret;
if (ret == 0) {
for (i = 0; i < count; i++) {
return ret;
int ret = 0;
if (parent_count == 0)
parent_idx = 0;
for (i = 0; i < rel_count; i++) {
while (diff > 0) {
parent_uid++;
diff--;
while (diff > 0) {
parent_uid++;
diff--;
return ret;
unsigned int *count_r)