mailbox-uidvalidity.c revision 0e881a691e6d2d2836d923ef3e92b14aeda60f0d
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "lib.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "ioloop.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "str.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "read-full.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "write-full.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include "mailbox-uidvalidity.h"
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include <stdio.h>
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include <stdlib.h>
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include <unistd.h>
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include <dirent.h>
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#include <fcntl.h>
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen#define RETRY_COUNT 10
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainenstatic uint32_t mailbox_uidvalidity_next_fallback(void)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen{
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen static uint32_t uid_validity = 0;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* we failed to use the uidvalidity file. don't fail the mailbox
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen creation because of it though, most of the time it's safe enough
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen to use the current time as the uidvalidity value. */
0e881a691e6d2d2836d923ef3e92b14aeda60f0dTimo Sirainen if (uid_validity < (uint32_t)ioloop_time)
0e881a691e6d2d2836d923ef3e92b14aeda60f0dTimo Sirainen uid_validity = (uint32_t)ioloop_time;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen else
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen uid_validity++;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return uid_validity;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen}
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainenstatic void mailbox_uidvalidity_write(const char *path, uint32_t uid_validity)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen{
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen char buf[8+1];
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen int fd;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fd = open(path, O_RDWR | O_CREAT, 0666);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (fd == -1) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("open(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_snprintf(buf, sizeof(buf), "%08x", uid_validity);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (pwrite_full(fd, buf, strlen(buf), 0) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("write(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (close(fd) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("close(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen}
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainenstatic int mailbox_uidvalidity_rename(const char *path, uint32_t *uid_validity)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen{
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen string_t *src, *dest;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen unsigned int i, prefix_len;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen int ret;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen src = t_str_new(256);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_append(src, path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen dest = t_str_new(256);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_append(dest, path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen prefix_len = str_len(src);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen for (i = 0; i < RETRY_COUNT; i++) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_printfa(src, ".%08x", *uid_validity);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen *uid_validity += 1;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_printfa(dest, ".%08x", *uid_validity);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if ((ret = rename(str_c(src), str_c(dest))) == 0 ||
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen errno != ENOENT)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen break;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* possibly a race condition. try the next value. */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_truncate(src, prefix_len);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen str_truncate(dest, prefix_len);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (ret < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("rename(%s, %s) failed: %m", str_c(src), str_c(dest));
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return ret;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen}
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainenstatic uint32_t mailbox_uidvalidity_next_rescan(const char *path)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen{
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen DIR *d;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen struct dirent *dp;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen const char *fname, *dir, *prefix, *tmp;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen char *endp;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen unsigned int i, prefix_len;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen uint32_t cur_value, min_value, max_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen int fd;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fname = strrchr(path, '/');
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (fname == NULL) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen dir = ".";
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fname = path;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen } else {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen dir = t_strdup_until(path, fname);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fname++;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen d = opendir(dir);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (d == NULL) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("opendir(%s) failed: %m", dir);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_fallback();
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen prefix = t_strconcat(fname, ".", NULL);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen prefix_len = strlen(prefix);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* just in case there happens to be multiple matching uidvalidity
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen files, track the min/max values. use the max value and delete the
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen min value file. */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen max_value = 0; min_value = (uint32_t)-1;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen while ((dp = readdir(d)) != NULL) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (strncmp(dp->d_name, prefix, prefix_len) == 0) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen cur_value = strtoul(dp->d_name + prefix_len, &endp, 16);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (*endp == '\0') {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (min_value > cur_value)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen min_value = cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (max_value < cur_value)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen max_value = cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (closedir(d) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("closedir(%s) failed: %m", dir);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (max_value == 0) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* no uidvalidity files. create one. */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen for (i = 0; i < RETRY_COUNT; i++) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen cur_value = mailbox_uidvalidity_next_fallback();
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen tmp = t_strdup_printf("%s.%08x", path, cur_value);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0666);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (fd != -1 || errno != EEXIST)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen break;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* already exists. although it's quite unlikely we'll
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen hit this race condition. more likely we'll create
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen a duplicate file.. */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (fd == -1) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("creat(%s) failed: %m", tmp);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen (void)close(fd);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen mailbox_uidvalidity_write(path, cur_value);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (min_value != max_value) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* duplicate uidvalidity files, delete the oldest */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen tmp = t_strdup_printf("%s.%08x", path, min_value);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (unlink(tmp) < 0 && errno != ENOENT)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("unlink(%s) failed: %m", tmp);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen cur_value = max_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (mailbox_uidvalidity_rename(path, &cur_value) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_fallback();
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen mailbox_uidvalidity_write(path, cur_value);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen}
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainenuint32_t mailbox_uidvalidity_next(const char *path)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen{
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen char buf[8+1], *endp;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen uint32_t cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen int fd, ret;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen fd = open(path, O_RDWR);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (fd == -1) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (errno != ENOENT)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("open(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_rescan(path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen ret = read_full(fd, buf, sizeof(buf)-1);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (ret < 0) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("read(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen (void)close(fd);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_rescan(path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen buf[sizeof(buf)-1] = 0;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen cur_value = strtoul(buf, &endp, 16);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (ret == 0 || endp != buf+sizeof(buf)-1) {
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* broken value */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen (void)close(fd);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_rescan(path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen }
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* we now have the current uidvalidity value that's hopefully correct */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (mailbox_uidvalidity_rename(path, &cur_value) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return mailbox_uidvalidity_next_rescan(path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen /* fast path succeeded. write the current value to the main
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen uidvalidity file. */
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_snprintf(buf, sizeof(buf), "%08x", cur_value);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (pwrite_full(fd, buf, strlen(buf), 0) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("write(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen if (close(fd) < 0)
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen i_error("close(%s) failed: %m", path);
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen return cur_value;
300baf826ba39003b861e7716c35bae372e6f23eTimo Sirainen}