<commit_before>/* Copyright (C) 2003-2004 <NAME> */

/*
   Locking is meant to be as transparent as possible. Anything that locks
   the index must either keep it only a short time, or be prepared that the
   lock is lost.

   Lock is lost in only one situation: when we try to get an exclusive lock
   but we already have a shared lock. Then we'll drop all shared locks and
   get the exclusive lock.

   Locking should never fail or timeout. Exclusive locks must be kept as short
   time as possible. Shared locks can be long living, so if can't get exclusive
   lock directly within 2 seconds, we'll replace the index file with a copy of
   it. That means the shared lock holders can keep using the old file while
   we're modifying the new file.

   lock_id is used to figure out if acquired lock is still valid. Shared
   locks have even numbers, exclusive locks have odd numbers. The number is
   increased by two every time the lock is dropped.

   mail_index_lock_shared() -> lock_id=2
   mail_index_lock_shared() -> lock_id=2
   mail_index_lock_exclusive() -> lock_id=5 (had to drop shared locks)
   mail_index_lock_shared() -> lock_id=4

   Only 4 and 5 locks are valid at this time.
*/

#include "lib.h"
#include "mmap-util.h"
#include "file-lock.h"
#include "write-full.h"
#include "mail-index-private.h"

#include <stdio.h>
#include <sys/stat.h>

static int mail_index_reopen(struct mail_index *index, int fd)
{
	int ret;

	mail_index_unmap(index, index->map);
	index->map = NULL;

	if (close(index->fd) < 0)
		mail_index_set_syscall_error(index, "close()");
	index->fd = fd;

	ret = fd < 0 ? mail_index_try_open(index, NULL) :
		mail_index_map(index, FALSE);
	if (ret <= 0) {
		// FIXME: serious problem, we'll just crash later..
		return -1;
	}

	return 0;
}

static int mail_index_has_changed(struct mail_index *index)
{
	struct stat st1, st2;

	if (fstat(index->fd, &st1) < 0)
		return mail_index_set_syscall_error(index, "fstat()");
	if (stat(index->filepath, &st2) < 0)
		return mail_index_set_syscall_error(index, "stat()");

	if (st1.st_ino != st2.st_ino ||
	    !CMP_DEV_T(st1.st_dev, st2.st_dev)) {
		if (mail_index_reopen(index, -1) < 0)
			return -1;
		return 1;
	} else {
		return 0;
	}
}

static int mail_index_lock_mprotect(struct mail_index *index, int lock_type)
{
	int prot;

	if (index->map != NULL &&
	    !MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
		prot = lock_type == F_UNLCK ? PROT_NONE :
			lock_type == F_WRLCK ? (PROT_READ|PROT_WRITE) :
			PROT_READ;
		if (mprotect(index->map->mmap_base,
			     index->map->file_size, prot) < 0) {
			mail_index_set_syscall_error(index, "mprotect()");
			return -1;
		}
	}
	return 0;
}

static int mail_index_lock(struct mail_index *index, int lock_type,
			   unsigned int timeout_secs, int update_index,
			   unsigned int *lock_id_r)
{
	int ret;

	i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);

	if (index->fcntl_locks_disable) {
		/* FIXME: exclusive locking will rewrite the index file every
		   time. shouldn't really be needed.. reading doesn't require
		   locks then, though */
		if (lock_type == F_WRLCK)
			return 0;
	} else {
	}

	if (lock_type == F_WRLCK && index->lock_type == F_RDLCK) {
		/* drop shared locks */
		i_assert(index->excl_lock_count == 0);

		if (file_wait_lock(index->fd, F_UNLCK) < 0)
			mail_index_set_syscall_error(index, "file_wait_lock()");

		index->shared_lock_count = 0;
		index->lock_type = F_UNLCK;
		index->lock_id += 2; /* make sure failures below work right */
	}

	if (index->excl_lock_count > 0 || index->shared_lock_count > 0) {
		i_assert(lock_type == F_RDLCK || index->excl_lock_count > 0);
		if (lock_type == F_RDLCK) {
			index->shared_lock_count++;
			*lock_id_r = index->lock_id;
		} else {
			index->excl_lock_count++;
			*lock_id_r = index->lock_id + 1;
		}
		return 1;
	}

	i_assert(index->lock_type == F_UNLCK);

	if (update_index && lock_type != F_WRLCK) {
		if (mail_index_has_changed(index) < 0)<commit_msg>whops, was unfinished<commit_after>/* Copyright (C) 2003-2004 <NAME> */

/*
   Locking is meant to be as transparent as possible. Anything that locks
   the index must either keep it only a short time, or be prepared that the
   lock is lost.

   Lock is lost in only one situation: when we try to get an exclusive lock
   but we already have a shared lock. Then we'll drop all shared locks and
   get the exclusive lock.

   Locking should never fail or timeout. Exclusive locks must be kept as short
   time as possible. Shared locks can be long living, so if can't get exclusive
   lock directly within 2 seconds, we'll replace the index file with a copy of
   it. That means the shared lock holders can keep using the old file while
   we're modifying the new file.

   lock_id is used to figure out if acquired lock is still valid. Shared
   locks have even numbers, exclusive locks have odd numbers. The number is
   increased by two every time the lock is dropped.

   mail_index_lock_shared() -> lock_id=2
   mail_index_lock_shared() -> lock_id=2
   mail_index_lock_exclusive() -> lock_id=5 (had to drop shared locks)
   mail_index_lock_shared() -> lock_id=4

   Only 4 and 5 locks are valid at this time.
*/

#include "lib.h"
#include "mmap-util.h"
#include "file-lock.h"
#include "write-full.h"
#include "mail-index-private.h"

#include <stdio.h>
#include <sys/stat.h>

static int mail_index_reopen(struct mail_index *index, int fd)
{
	int ret;

	mail_index_unmap(index, index->map);
	index->map = NULL;

	if (close(index->fd) < 0)
		mail_index_set_syscall_error(index, "close()");
	index->fd = fd;

	ret = fd < 0 ? mail_index_try_open(index, NULL) :
		mail_index_map(index, FALSE);
	if (ret <= 0) {
		// FIXME: serious problem, we'll just crash later..
		return -1;
	}

	return 0;
}

static int mail_index_has_changed(struct mail_index *index)
{
	struct stat st1, st2;

	if (fstat(index->fd, &st1) < 0)
		return mail_index_set_syscall_error(index, "fstat()");
	if (stat(index->filepath, &st2) < 0)
		return mail_index_set_syscall_error(index, "stat()");

	if (st1.st_ino != st2.st_ino ||
	    !CMP_DEV_T(st1.st_dev, st2.st_dev)) {
		if (mail_index_reopen(index, -1) < 0)
			return -1;
		return 1;
	} else {
		return 0;
	}
}

static int mail_index_lock_mprotect(struct mail_index *index, int lock_type)
{
	int prot;

	if (index->map != NULL &&
	    !MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
		prot = lock_type == F_UNLCK ? PROT_NONE :
			lock_type == F_WRLCK ? (PROT_READ|PROT_WRITE) :
			PROT_READ;
		if (mprotect(index->map->mmap_base,
			     index->map->file_size, prot) < 0) {
			mail_index_set_syscall_error(index, "mprotect()");
			return -1;
		}
	}
	return 0;
}

static int mail_index_lock(struct mail_index *index, int lock_type,
			   unsigned int timeout_secs, int update_index,
			   unsigned int *lock_id_r)
{
	int ret;

	i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);

	if (index->fcntl_locks_disable) {
		/* FIXME: exclusive locking will rewrite the index file every
		   time. shouldn't really be needed.. reading doesn't require
		   locks then, though */
		if (lock_type == F_WRLCK)
			return 0;
		if (mail_index_lock_mprotect(index, lock_type) < 0)
			return -1;
		return 1;
	}

	if (lock_type == F_WRLCK && index->lock_type == F_RDLCK) {
		/* drop shared locks */
		i_assert(index->excl_lock_count == 0);

		if (file_wait_lock(index->fd, F_UNLCK) < 0)
			mail_index_set_syscall_error(index, "file_wait_lock()");

		index->shared_lock_count = 0;
		index->lock_type = F_UNLCK;
		index->lock_id += 2; /* make sure failures below work right */
	}

	if (index->excl_lock_count > 0 || index->shared_lock_count > 0) {
		i_assert(lock_type == F_RDLCK || index->excl_lock_count > 0);
		if (lock_type == F_RDLCK) {
			index->shared_lock_count++;
			*lock_id_r = index->lock_id;
		} else {
			index->excl_lock_count++;
			*lock_id_r = index->lock_id + 1;
		}
		return 1;
	}

	i_assert(index->lock_type == F_UNLCK);

	if (update_index && lock_type != F_WRLCK) {
		if (mail_index_has_changed(index) < 0)
{'fraction_non_alphanumeric': 0.11598196392785572, 'fraction_numerical': 0.012650300601202405, 'mean_word_length': 3.7529761904761907, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4869234', 'n_tokens_mistral': 3167, 'n_tokens_neox': 2873, 'n_words': 846}