comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:3b1985cbc908
1 /* Copyright (C) 2002 Timo Sirainen */
2
3 #include "lib.h"
4 #include "hostpid.h"
5 #include "unlink-lockfiles.h"
6 #include "mail-index.h"
7 #include "mail-index-util.h"
8 #include "mail-lockdir.h"
9
10 #include <time.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sys/stat.h>
15
16 #define DIRLOCK_FILE_PREFIX ".imap.dirlock"
17
18 /* 0.1 .. 0.2msec */
19 #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
20
21 /* The dirlock should be used only while creating the index file. After the
22 header is written, the file itself should be locked and dirlock dropped
23 before index is built. So, this value shouldn't be very large, probably
24 even a few seconds would more than enough but we'll use a safe 10 seconds
25 by default. */
26 #define MAX_LOCK_WAIT_SECONDS 10
27
28 /* Non-local locks have a life time of 30 minutes, just to be sure that
29 small clock differences won't break things. */
30 #define NFS_LOCK_TIMEOUT (60*30)
31
32 static int mail_index_cleanup_dir_locks(const char *dir)
33 {
34 const char *hostprefix, *path;
35 struct stat st;
36
37 hostprefix = t_strconcat(DIRLOCK_FILE_PREFIX ".",
38 my_hostname, ".", NULL);
39
40 unlink_lockfiles(dir, hostprefix, DIRLOCK_FILE_PREFIX ".",
41 time(NULL) - NFS_LOCK_TIMEOUT);
42
43 /* if hard link count has dropped to 1, we've unlocked the file */
44 path = t_strconcat(dir, "/" DIRLOCK_FILE_PREFIX, NULL);
45 if (stat(path, &st) == 0 && st.st_nlink == 1) {
46 /* only itself, safe to delete */
47 (void)unlink(path);
48 return TRUE;
49 }
50
51 return FALSE;
52 }
53
54 static int mail_index_unlock_dir(MailIndex *index, const char *path,
55 const char *lockpath)
56 {
57 struct stat st, lockst;
58
59 if (stat(lockpath, &st) != 0) {
60 index_set_error(index, "stat() failed for lock file %s: %m",
61 lockpath);
62 return FALSE;
63 }
64
65 if (st.st_nlink > 1) {
66 /* make sure we're really the one who's locked it */
67 if (stat(path, &lockst) != 0) {
68 index_set_error(index, "stat() failed for lock file "
69 "%s: %m", path);
70 return FALSE;
71 }
72
73 if (st.st_dev != lockst.st_dev ||
74 st.st_ino != lockst.st_ino) {
75 index_set_error(index, "Unlocking file %s failed: "
76 "we're not the lock owner "
77 "(%lu,%lu vs %lu,%lu)", lockpath,
78 (unsigned long) st.st_dev,
79 (unsigned long) st.st_ino,
80 (unsigned long) lockst.st_dev,
81 (unsigned long) lockst.st_ino);
82 return FALSE;
83 }
84 }
85
86 /* first unlink the actual lock file */
87 if (unlink(lockpath) == -1) {
88 index_set_error(index, "unlink() failed for lock file %s: %m",
89 lockpath);
90 return FALSE;
91 }
92
93 (void)unlink(path);
94 return TRUE;
95 }
96
97 int mail_index_lock_dir(MailIndex *index, MailLockType lock_type)
98 {
99 struct stat st;
100 const char *path, *lockpath;
101 int fd, orig_errno, first;
102 time_t max_wait_time;
103
104 i_assert(lock_type == MAIL_LOCK_EXCLUSIVE ||
105 lock_type == MAIL_LOCK_UNLOCK);
106
107 hostpid_init();
108
109 /* use .dirlock.host.pid as our lock indicator file and
110 .dirlock as the real lock */
111 path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".",
112 my_hostname, ".", my_pid, NULL);
113 lockpath = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX, NULL);
114
115 if (lock_type == MAIL_LOCK_UNLOCK)
116 return mail_index_unlock_dir(index, path, lockpath);
117
118 (void)unlink(path);
119 fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
120 if (fd == -1) {
121 index_set_error(index, "Can't create lock file %s: %m", path);
122 return FALSE;
123 }
124
125 /* try to link the file into lock file. */
126 first = TRUE; max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS;
127 while (link(path, lockpath) == -1) {
128 if (errno != EEXIST) {
129 orig_errno = errno;
130
131 /* NFS may die and link() fail even if it really
132 was created */
133 if (stat(path, &st) == 0 && st.st_nlink == 2)
134 break;
135
136 index_set_error(index, "link(%s, %s) lock failed: %m",
137 path, lockpath);
138 return FALSE;
139 }
140
141 if (first) {
142 /* cleanup lock files once */
143 first = FALSE;
144 if (mail_index_cleanup_dir_locks(index->dir))
145 continue; /* lock was deleted, try again */
146 }
147
148 if (time(NULL) > max_wait_time) {
149 index_set_error(index, "Timeout waiting lock in "
150 "directory %s", index->dir);
151 return FALSE;
152 }
153
154 usleep(LOCK_RANDOM_USLEEP_TIME);
155 }
156
157 return TRUE;
158 }