view src/lib-storage/index/maildir/maildir-util.c @ 6885:a22b1a72ea89 HEAD

Code cleanup
author Timo Sirainen <tss@iki.fi>
date Sat, 01 Dec 2007 10:31:02 +0200
parents 186b164a9579
children 82420ed23379
line wrap: on
line source

/* Copyright (c) 2004-2007 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "str.h"
#include "mkdir-parents.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-sync.h"

#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <utime.h>
#include <sys/stat.h>

#define MAILDIR_RESYNC_RETRY_COUNT 10

static int maildir_file_do_try(struct maildir_mailbox *mbox, uint32_t uid,
			       maildir_file_do_func *callback, void *context)
{
	const char *fname, *path;
        enum maildir_uidlist_rec_flag flags;
	int ret;

	fname = maildir_uidlist_lookup(mbox->uidlist, uid, &flags);
	if (fname == NULL)
		return -2; /* expunged */

	t_push();
	if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
		/* probably in new/ dir */
		path = t_strconcat(mbox->path, "/new/", fname, NULL);
		ret = callback(mbox, path, context);
		if (ret != 0) {
			t_pop();
			return ret;
		}
	}

	path = t_strconcat(mbox->path, "/cur/", fname, NULL);
	ret = callback(mbox, path, context);
	t_pop();
	return ret;
}

static int do_racecheck(struct maildir_mailbox *mbox, const char *path,
			void *context ATTR_UNUSED)
{
	struct stat st;

	if (lstat(path, &st) == 0 && (st.st_mode & S_IFLNK) != 0) {
		/* most likely a symlink pointing to a non-existing file */
		mail_storage_set_critical(&mbox->storage->storage,
			"Maildir: Symlink destination doesn't exist: %s", path);
		return -2;
	} else {
		mail_storage_set_critical(&mbox->storage->storage,
			"maildir_file_do(%s): Filename keeps changing", path);
		return -1;
	}
}

#undef maildir_file_do
int maildir_file_do(struct maildir_mailbox *mbox, uint32_t uid,
		    maildir_file_do_func *callback, void *context)
{
	int i, ret;

	ret = maildir_file_do_try(mbox, uid, callback, context);
	for (i = 0; i < MAILDIR_RESYNC_RETRY_COUNT && ret == 0; i++) {
		/* file is either renamed or deleted. sync the maildir and
		   see which one. if file appears to be renamed constantly,
		   don't try to open it more than 10 times. */
		if (maildir_storage_sync_force(mbox) < 0)
			return -1;

		ret = maildir_file_do_try(mbox, uid, callback, context);
	}

	if (i == MAILDIR_RESYNC_RETRY_COUNT)
		ret = maildir_file_do_try(mbox, uid, do_racecheck, context);

	return ret == -2 ? 0 : ret;
}

void maildir_tmp_cleanup(struct mail_storage *storage, const char *dir)
{
	DIR *dirp;
	struct dirent *d;
	struct stat st;
	string_t *path;
	unsigned int dir_len;

	dirp = opendir(dir);
	if (dirp == NULL) {
		if (errno != ENOENT) {
			mail_storage_set_critical(storage,
				"opendir(%s) failed: %m", dir);
		}
		return;
	}

	t_push();
	path = t_str_new(256);
	str_printfa(path, "%s/", dir);
	dir_len = str_len(path);

	while ((d = readdir(dirp)) != NULL) {
		if (d->d_name[0] == '.' &&
		    (d->d_name[1] == '\0' ||
		     (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
			/* skip . and .. */
			continue;
		}

		str_truncate(path, dir_len);
		str_append(path, d->d_name);
		if (stat(str_c(path), &st) < 0) {
			if (errno != ENOENT) {
				mail_storage_set_critical(storage,
					"stat(%s) failed: %m", str_c(path));
			}
		} else if (st.st_ctime <=
			   ioloop_time - MAILDIR_TMP_DELETE_SECS) {
			if (unlink(str_c(path)) < 0 && errno != ENOENT) {
				mail_storage_set_critical(storage,
					"unlink(%s) failed: %m", str_c(path));
			}
		}
	}
	t_pop();

#ifdef HAVE_DIRFD
	if (fstat(dirfd(dirp), &st) < 0) {
		mail_storage_set_critical(storage,
			"fstat(%s) failed: %m", dir);
	}
#else
	if (stat(dir, &st) < 0) {
		mail_storage_set_critical(storage,
			"stat(%s) failed: %m", dir);
	}
#endif
	else if (st.st_atime < ioloop_time) {
		/* mounted with noatime. update it ourself. */
		if (utime(dir, NULL) < 0 && errno != ENOENT) {
			mail_storage_set_critical(storage,
				"utime(%s) failed: %m", dir);
		}
	}

	if (closedir(dirp) < 0) {
		mail_storage_set_critical(storage,
			"closedir(%s) failed: %m", dir);
	}
}

static int maildir_create_subdirs(struct maildir_mailbox *mbox)
{
	static const char *subdirs[] = { "cur", "new", "tmp" };
	const char *dirs[N_ELEMENTS(subdirs) + 2];
	struct mailbox *box = &mbox->ibox.box;
	struct stat st;
	const char *path;
	unsigned int i;

	t_push();
	/* @UNSAFE: get a list of directories we want to create */
	for (i = 0; i < N_ELEMENTS(subdirs); i++)
		dirs[i] = t_strconcat(mbox->path, "/", subdirs[i], NULL);
	dirs[i++] = mail_storage_get_mailbox_control_dir(box->storage,
							 box->name);
	dirs[i++] = mail_storage_get_mailbox_index_dir(box->storage,
						       box->name);
	i_assert(i == N_ELEMENTS(dirs));

	for (i = 0; i < N_ELEMENTS(dirs); i++) {
		path = dirs[i];
		if (path == NULL || stat(path, &st) == 0)
			continue;
		if (errno != ENOENT) {
			mail_storage_set_critical(box->storage,
						  "stat(%s) failed: %m", path);
			break;
		}
		if (mkdir_parents(path, box->dir_create_mode) < 0 &&
		    errno != EEXIST) {
			if (errno == ENOENT) {
				/* mailbox was being deleted just now */
				mailbox_set_deleted(box);
				break;
			}
			mail_storage_set_critical(box->storage,
						  "mkdir(%s) failed: %m", path);
			break;
		}
	}
	t_pop();
	return i == N_ELEMENTS(dirs) ? 0 : -1;
}

bool maildir_set_deleted(struct maildir_mailbox *mbox)
{
	struct mailbox *box = &mbox->ibox.box;
	struct stat st;

	if (stat(mbox->path, &st) < 0) {
		if (errno == ENOENT)
			mailbox_set_deleted(box);
		else {
			mail_storage_set_critical(box->storage,
				"stat(%s) failed: %m", mbox->path);
		}
		return TRUE;
	}
	/* maildir itself exists. create all of its subdirectories in case
	   they got lost. */
	return maildir_create_subdirs(mbox) < 0 ? TRUE : FALSE;
}