예제 #1
0
static int
keywords_update_records(struct mail_index_sync_map_ctx *ctx,
			const struct mail_index_ext *ext,
			unsigned int keyword_idx, enum modify_type type,
			uint32_t uid1, uint32_t uid2)
{
	struct mail_index_view *view = ctx->view;
	struct mail_index_record *rec;
	unsigned char *data, data_mask;
	unsigned int data_offset;
	uint32_t seq1, seq2;

	i_assert(keyword_idx != UINT_MAX);

	if (!mail_index_lookup_seq_range(view, uid1, uid2, &seq1, &seq2))
		return 1;

	mail_index_modseq_update_keyword(ctx->modseq_ctx, keyword_idx,
					  seq1, seq2);

	data_offset = keyword_idx / CHAR_BIT;
	data_mask = 1 << (keyword_idx % CHAR_BIT);

	i_assert(data_offset < ext->record_size);
	data_offset += ext->record_offset;

	i_assert(data_offset >= MAIL_INDEX_RECORD_MIN_SIZE);

	switch (type) {
	case MODIFY_ADD:
		for (; seq1 <= seq2; seq1++) {
			rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq1);
			data = PTR_OFFSET(rec, data_offset);
			*data |= data_mask;
		}
		break;
	case MODIFY_REMOVE:
		data_mask = ~data_mask;
		for (; seq1 <= seq2; seq1++) {
			rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq1);
			data = PTR_OFFSET(rec, data_offset);
			*data &= data_mask;
		}
		break;
	default:
		i_unreached();
	}
	return 1;
}
예제 #2
0
int
mail_index_sync_keywords_reset(struct mail_index_sync_map_ctx *ctx,
			       const struct mail_transaction_header *hdr,
			       const struct mail_transaction_keyword_reset *r)
{
	struct mail_index_map *map = ctx->view->map;
	struct mail_index_record *rec;
	const struct mail_index_ext *ext;
	const struct mail_transaction_keyword_reset *end;
	uint32_t ext_map_idx, seq1, seq2;

	if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS,
				       &ext_map_idx)) {
		/* nothing to do */
		return 1;
	}

	ext = array_idx(&map->extensions, ext_map_idx);
	end = CONST_PTR_OFFSET(r, hdr->size);
	for (; r != end; r++) {
		if (!mail_index_lookup_seq_range(ctx->view, r->uid1, r->uid2,
						 &seq1, &seq2))
			continue;

		mail_index_modseq_reset_keywords(ctx->modseq_ctx, seq1, seq2);
		for (; seq1 <= seq2; seq1++) {
			rec = MAIL_INDEX_REC_AT_SEQ(map, seq1);
			memset(PTR_OFFSET(rec, ext->record_offset),
			       0, ext->record_size);
		}
	}
	return 1;
}
예제 #3
0
static void view_lookup_first(struct mail_index_view *view,
			      enum mail_flags flags, uint8_t flags_mask,
			      uint32_t *seq_r)
{
#define LOW_UPDATE(x) \
	STMT_START { if ((x) > low_uid) low_uid = x; } STMT_END
	const struct mail_index_header *hdr = &view->map->hdr;
	const struct mail_index_record *rec;
	uint32_t seq, seq2, low_uid = 1;

	*seq_r = 0;

	if ((flags_mask & MAIL_SEEN) != 0 && (flags & MAIL_SEEN) == 0)
		LOW_UPDATE(hdr->first_unseen_uid_lowwater);
	if ((flags_mask & MAIL_DELETED) != 0 && (flags & MAIL_DELETED) != 0)
		LOW_UPDATE(hdr->first_deleted_uid_lowwater);

	if (low_uid == 1)
		seq = 1;
	else {
		if (!mail_index_lookup_seq_range(view, low_uid, hdr->next_uid,
						 &seq, &seq2))
			return;
	}

	i_assert(hdr->messages_count <= view->map->rec_map->records_count);
	for (; seq <= hdr->messages_count; seq++) {
		rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq);
		if ((rec->flags & flags_mask) == (uint8_t)flags) {
			*seq_r = seq;
			break;
		}
	}
}
예제 #4
0
static void view_lookup_uid(struct mail_index_view *view, uint32_t seq,
			    uint32_t *uid_r)
{
	i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));

	*uid_r = MAIL_INDEX_REC_AT_SEQ(view->map, seq)->uid;
}
예제 #5
0
static void mail_index_map_clear_recent_flags(struct mail_index_map *map)
{
	struct mail_index_record *rec;
	uint32_t seq;

	for (seq = 1; seq <= map->hdr.messages_count; seq++) {
		rec = MAIL_INDEX_REC_AT_SEQ(map, seq);
		rec->flags &= ~MAIL_RECENT;
	}
}
예제 #6
0
int mail_index_map_check_header(struct mail_index_map *map,
				const char **error_r)
{
	struct mail_index *index = map->index;
	const struct mail_index_header *hdr = &map->hdr;

	if (!mail_index_check_header_compat(index, hdr, (uoff_t)-1, error_r))
		return -1;

	/* following some extra checks that only take a bit of CPU */
	if (hdr->record_size < sizeof(struct mail_index_record)) {
		*error_r = t_strdup_printf(
			"record_size too small (%u < %"PRIuSIZE_T")",
			hdr->record_size, sizeof(struct mail_index_record));
		return -1;
	}

	if (hdr->uid_validity == 0 && hdr->next_uid != 1) {
		*error_r = t_strdup_printf(
			"uidvalidity=0, but next_uid=%u", hdr->next_uid);
		return 0;
	}
	if (hdr->next_uid == 0) {
		*error_r = "next_uid=0";
		return 0;
	}
	if (hdr->messages_count > map->rec_map->records_count) {
		*error_r = t_strdup_printf(
			"messages_count is higher in header than record map (%u > %u)",
			hdr->messages_count, map->rec_map->records_count);
		return 0;
	}

	if (hdr->seen_messages_count > hdr->messages_count) {
		*error_r = t_strdup_printf(
			"seen_messages_count %u > messages_count %u",
			hdr->seen_messages_count, hdr->messages_count);
		return 0;
	}
	if (hdr->deleted_messages_count > hdr->messages_count) {
		*error_r = t_strdup_printf(
			"deleted_messages_count %u > messages_count %u",
			hdr->deleted_messages_count, hdr->messages_count);
		return 0;
	}
	switch (hdr->minor_version) {
	case 0:
		/* upgrade silently from v1.0 */
		map->hdr.unused_old_recent_messages_count = 0;
		if (hdr->first_recent_uid == 0)
			map->hdr.first_recent_uid = 1;
		index->need_recreate = TRUE;
		/* fall through */
	case 1:
		/* pre-v1.1.rc6: make sure the \Recent flags are gone */
		mail_index_map_clear_recent_flags(map);
		map->hdr.minor_version = MAIL_INDEX_MINOR_VERSION;
		/* fall through */
	case 2:
		/* pre-v2.2 (although should have been done in v2.1 already):
		   make sure the old unused fields are cleared */
		map->hdr.unused_old_sync_size = 0;
		map->hdr.unused_old_sync_stamp = 0;
	}
	if (hdr->first_recent_uid == 0) {
		*error_r = "first_recent_uid=0";
		return 0;
	}
	if (hdr->first_recent_uid > hdr->next_uid) {
		*error_r = t_strdup_printf(
			"first_recent_uid %u > next_uid %u",
			hdr->first_recent_uid, hdr->next_uid);
		return 0;
	}
	if (hdr->first_unseen_uid_lowwater > hdr->next_uid) {
		*error_r = t_strdup_printf(
			"first_unseen_uid_lowwater %u > next_uid %u",
			hdr->first_unseen_uid_lowwater, hdr->next_uid);
		return 0;
	}
	if (hdr->first_deleted_uid_lowwater > hdr->next_uid) {
		*error_r = t_strdup_printf(
			"first_deleted_uid_lowwater %u > next_uid %u",
			hdr->first_deleted_uid_lowwater, hdr->next_uid);
		return 0;
	}

	if (hdr->messages_count > 0) {
		/* last message's UID must be smaller than next_uid.
		   also make sure it's not zero. */
		const struct mail_index_record *rec;

		rec = MAIL_INDEX_REC_AT_SEQ(map, hdr->messages_count);
		if (rec->uid == 0) {
			*error_r = "last message has uid=0";
			return -1;
		}
		if (rec->uid >= hdr->next_uid) {
			*error_r = t_strdup_printf(
				"last message uid %u >= next_uid %u",
				rec->uid, hdr->next_uid);
			return 0;
		}
	}
	return 1;
}
예제 #7
0
static const struct mail_index_record *
view_lookup_full(struct mail_index_view *view, uint32_t seq,
		 struct mail_index_map **map_r, bool *expunged_r)
{
	static struct mail_index_record broken_rec;
	struct mail_index_map *map;
	const struct mail_index_record *rec, *head_rec;

	i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));

	/* look up the record */
	rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq);
	if (unlikely(rec->uid == 0)) {
		if (!view->inconsistent) {
			mail_index_set_error(view->index,
				"Corrupted Index file %s: Record [%u].uid=0",
				view->index->filepath, seq);
			(void)mail_index_fsck(view->index);
			view->inconsistent = TRUE;
		}

		/* we'll need to return something so the caller doesn't crash */
		*map_r = view->map;
		if (expunged_r != NULL)
			*expunged_r = TRUE;
		return &broken_rec;
	}
	if (view->map == view->index->map) {
		/* view's mapping is latest. we can use it directly. */
		*map_r = view->map;
		if (expunged_r != NULL)
			*expunged_r = FALSE;
		return rec;
	}

	/* look up the record from head mapping. it may contain some changes.

	   start looking up from the same sequence as in the old view.
	   if there are no expunges, it's there. otherwise it's somewhere
	   before (since records can't be inserted).

	   usually there are only a few expunges, so just going downwards from
	   our initial sequence position is probably faster than binary
	   search. */
	if (seq > view->index->map->hdr.messages_count)
		seq = view->index->map->hdr.messages_count;
	if (seq == 0) {
		/* everything is expunged from head. use the old record. */
		*map_r = view->map;
		if (expunged_r != NULL)
			*expunged_r = TRUE;
		return rec;
	}

	map = view->index->map;
	do {
		head_rec = MAIL_INDEX_REC_AT_SEQ(map, seq);
		if (head_rec->uid <= rec->uid)
			break;
	} while (--seq > 0);

	if (head_rec->uid == rec->uid) {
		/* found it. use it. reference the index mapping so that the
		   returned record doesn't get invalidated after next sync. */
		mail_index_view_ref_map(view, view->index->map);
		*map_r = view->index->map;
		if (expunged_r != NULL)
			*expunged_r = FALSE;
		return head_rec;
	} else {
		/* expuned from head. use the old record. */
		*map_r = view->map;
		if (expunged_r != NULL)
			*expunged_r = TRUE;
		return rec;
	}
}