0
|
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 }
|