Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-index/mail-lockdir.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | ca6967899c05 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "hostpid.h" #include "unlink-lockfiles.h" #include "mail-index.h" #include "mail-index-util.h" #include "mail-lockdir.h" #include <time.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #define DIRLOCK_FILE_PREFIX ".imap.dirlock" /* 0.1 .. 0.2msec */ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) /* The dirlock should be used only while creating the index file. After the header is written, the file itself should be locked and dirlock dropped before index is built. So, this value shouldn't be very large, probably even a few seconds would more than enough but we'll use a safe 10 seconds by default. */ #define MAX_LOCK_WAIT_SECONDS 10 /* Non-local locks have a life time of 30 minutes, just to be sure that small clock differences won't break things. */ #define NFS_LOCK_TIMEOUT (60*30) static int mail_index_cleanup_dir_locks(const char *dir) { const char *hostprefix, *path; struct stat st; hostprefix = t_strconcat(DIRLOCK_FILE_PREFIX ".", my_hostname, ".", NULL); unlink_lockfiles(dir, hostprefix, DIRLOCK_FILE_PREFIX ".", time(NULL) - NFS_LOCK_TIMEOUT); /* if hard link count has dropped to 1, we've unlocked the file */ path = t_strconcat(dir, "/" DIRLOCK_FILE_PREFIX, NULL); if (stat(path, &st) == 0 && st.st_nlink == 1) { /* only itself, safe to delete */ (void)unlink(path); return TRUE; } return FALSE; } static int mail_index_unlock_dir(MailIndex *index, const char *path, const char *lockpath) { struct stat st, lockst; if (stat(lockpath, &st) != 0) { index_set_error(index, "stat() failed for lock file %s: %m", lockpath); return FALSE; } if (st.st_nlink > 1) { /* make sure we're really the one who's locked it */ if (stat(path, &lockst) != 0) { index_set_error(index, "stat() failed for lock file " "%s: %m", path); return FALSE; } if (st.st_dev != lockst.st_dev || st.st_ino != lockst.st_ino) { index_set_error(index, "Unlocking file %s failed: " "we're not the lock owner " "(%lu,%lu vs %lu,%lu)", lockpath, (unsigned long) st.st_dev, (unsigned long) st.st_ino, (unsigned long) lockst.st_dev, (unsigned long) lockst.st_ino); return FALSE; } } /* first unlink the actual lock file */ if (unlink(lockpath) == -1) { index_set_error(index, "unlink() failed for lock file %s: %m", lockpath); return FALSE; } (void)unlink(path); return TRUE; } int mail_index_lock_dir(MailIndex *index, MailLockType lock_type) { struct stat st; const char *path, *lockpath; int fd, orig_errno, first; time_t max_wait_time; i_assert(lock_type == MAIL_LOCK_EXCLUSIVE || lock_type == MAIL_LOCK_UNLOCK); hostpid_init(); /* use .dirlock.host.pid as our lock indicator file and .dirlock as the real lock */ path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".", my_hostname, ".", my_pid, NULL); lockpath = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX, NULL); if (lock_type == MAIL_LOCK_UNLOCK) return mail_index_unlock_dir(index, path, lockpath); (void)unlink(path); fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660); if (fd == -1) { index_set_error(index, "Can't create lock file %s: %m", path); return FALSE; } /* try to link the file into lock file. */ first = TRUE; max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS; while (link(path, lockpath) == -1) { if (errno != EEXIST) { orig_errno = errno; /* NFS may die and link() fail even if it really was created */ if (stat(path, &st) == 0 && st.st_nlink == 2) break; index_set_error(index, "link(%s, %s) lock failed: %m", path, lockpath); return FALSE; } if (first) { /* cleanup lock files once */ first = FALSE; if (mail_index_cleanup_dir_locks(index->dir)) continue; /* lock was deleted, try again */ } if (time(NULL) > max_wait_time) { index_set_error(index, "Timeout waiting lock in " "directory %s", index->dir); return FALSE; } usleep(LOCK_RANDOM_USLEEP_TIME); } return TRUE; }