Mercurial > dovecot > original-hg > dovecot-1.2
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 } |