New modes, including a retry mode (the default), removed fcntl
and stub read/write lock mode. New function, to remove a lock even if not owned (useful for the external dotlock utility). New functions, to set the expire time for MU_LOCKER_TIME, the flags, and the retries and retry sleep for MU_LOCKER_RETRY.
Showing
2 changed files
with
430 additions
and
125 deletions
... | @@ -28,23 +28,49 @@ extern "C" { | ... | @@ -28,23 +28,49 @@ extern "C" { |
28 | struct _locker; | 28 | struct _locker; |
29 | typedef struct _locker *locker_t; | 29 | typedef struct _locker *locker_t; |
30 | 30 | ||
31 | extern int locker_create __P ((locker_t *, const char *filename, | 31 | /* lock expiry time */ |
32 | size_t len, int flags)); | 32 | #define MU_LOCKER_EXPIRE_TIME (10 * 60) |
33 | #define MU_LOCKER_RETRIES (10) | ||
34 | #define MU_LOCKER_RETRY_SLEEP (1) | ||
35 | |||
36 | /* locker_create() flags */ | ||
37 | |||
38 | #define MU_LOCKER_RETRY 0x01 | ||
39 | /* This requests that we loop retries times, sleeping retry_sleep | ||
40 | seconds in between trying to obtain the lock before failing with | ||
41 | MU_LOCK_CONFLICT. */ | ||
42 | #define MU_LOCKER_TIME 0x02 | ||
43 | /* This mode checks the last update time of the lock, then removes | ||
44 | it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this, | ||
45 | then the servers better periodically update the lock on the | ||
46 | file... do they? */ | ||
47 | #define MU_LOCKER_PID 0x04 | ||
48 | /* PID locking is only useful for programs that aren't using | ||
49 | an external dotlocker, non-setgid programs will use a dotlocker, | ||
50 | which locks and exits imediately. This is a protection against | ||
51 | a server crashing, it's not generally useful. */ | ||
52 | |||
53 | #define MU_LOCKER_DEFAULT (MU_LOCKER_RETRY) | ||
54 | |||
55 | extern int locker_create __P ((locker_t *, const char *filename, int flags)); | ||
33 | extern void locker_destroy __P ((locker_t *)); | 56 | extern void locker_destroy __P ((locker_t *)); |
34 | 57 | ||
35 | #define MU_LOCKER_RDLOCK 0 | 58 | /* Time is measured in seconds. */ |
36 | #define MU_LOCKER_WRLOCK 1 | ||
37 | 59 | ||
38 | /* locking flags */ | 60 | extern int locker_set_flags __P ((locker_t, int)); |
39 | #define MU_LOCKER_PID 1 | 61 | extern int locker_set_expire_time __P ((locker_t, int)); |
40 | #define MU_LOCKER_FCNTL 2 | 62 | extern int locker_set_retries __P ((locker_t, int)); |
41 | #define MU_LOCKER_TIME 4 | 63 | extern int locker_set_retry_sleep __P ((locker_t, int)); |
42 | 64 | ||
43 | #define MU_LOCKER_EXPIRE_TIME (5 * 60) | 65 | extern int locker_get_flags __P ((locker_t, int*)); |
66 | extern int locker_get_expire_time __P ((locker_t, int*)); | ||
67 | extern int locker_get_retries __P ((locker_t, int*)); | ||
68 | extern int locker_get_retry_sleep __P ((locker_t, int*)); | ||
44 | 69 | ||
45 | extern int locker_lock __P ((locker_t, int flag)); | 70 | extern int locker_lock __P ((locker_t)); |
46 | extern int locker_touchlock __P ((locker_t)); | 71 | extern int locker_touchlock __P ((locker_t)); |
47 | extern int locker_unlock __P ((locker_t)); | 72 | extern int locker_unlock __P ((locker_t)); |
73 | extern int locker_remove_lock __P ((locker_t)); | ||
48 | 74 | ||
49 | #ifdef __cplusplus | 75 | #ifdef __cplusplus |
50 | } | 76 | } | ... | ... |
... | @@ -19,61 +19,104 @@ | ... | @@ -19,61 +19,104 @@ |
19 | # include <config.h> | 19 | # include <config.h> |
20 | #endif | 20 | #endif |
21 | 21 | ||
22 | #include <assert.h> | ||
22 | #include <errno.h> | 23 | #include <errno.h> |
23 | #include <sys/types.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | #include <stdio.h> | ||
27 | #include <fcntl.h> | 24 | #include <fcntl.h> |
28 | #include <limits.h> | 25 | #include <limits.h> |
29 | #include <sys/stat.h> | 26 | #include <signal.h> |
30 | #include <unistd.h> | 27 | #include <stdio.h> |
28 | #include <stdlib.h> | ||
29 | #include <string.h> | ||
31 | #include <time.h> | 30 | #include <time.h> |
31 | #include <unistd.h> | ||
32 | #include <utime.h> | 32 | #include <utime.h> |
33 | #include <signal.h> | 33 | |
34 | #include <sys/types.h> | ||
35 | #include <sys/stat.h> | ||
34 | 36 | ||
35 | #include <mailutils/locker.h> | 37 | #include <mailutils/locker.h> |
38 | #include <mailutils/errno.h> | ||
36 | 39 | ||
37 | #define LOCKFILE_ATTR 0444 | 40 | #define LOCKFILE_ATTR 0444 |
38 | 41 | ||
39 | /* First draft by Brian Edmond. */ | 42 | /* First draft by Brian Edmond. */ |
43 | /* For subsequent modifications, see the GNU mailutils ChangeLog. */ | ||
40 | 44 | ||
41 | struct _locker | 45 | struct _locker |
42 | { | 46 | { |
43 | int fd; | 47 | int fd; |
44 | int refcnt; | 48 | int refcnt; |
45 | char *fname; | 49 | |
50 | char *file; | ||
51 | |||
52 | char *dotlock; | ||
53 | |||
46 | int flags; | 54 | int flags; |
55 | |||
56 | int expire_time; | ||
57 | int retries; | ||
58 | int retry_sleep; | ||
47 | }; | 59 | }; |
48 | 60 | ||
61 | /* Assert that we're managing the refcnt and fd correctly, either | ||
62 | * we have a lock, and the fd is valid, or refcnt is 0 and fd is -1. | ||
63 | * And refcnt can never be less than 0. | ||
64 | */ | ||
65 | #define INVARIANT(l) \ | ||
66 | assert((l)->refcnt >= 0); \ | ||
67 | assert( \ | ||
68 | (((l)->refcnt > 0) && ((l)->fd != -1)) || \ | ||
69 | (((l)->refcnt == 0) && ((l)->fd == -1)) \ | ||
70 | ); | ||
71 | |||
49 | int | 72 | int |
50 | locker_create (locker_t *plocker, const char *filename, size_t len, int flags) | 73 | locker_create (locker_t *plocker, const char *filename, int flags) |
51 | { | 74 | { |
52 | locker_t l; | 75 | locker_t l; |
53 | 76 | ||
54 | if (plocker == NULL || filename == NULL || len == 0) | 77 | if (plocker == NULL) |
78 | return MU_ERR_OUT_PTR_NULL; | ||
79 | |||
80 | if(filename == NULL) | ||
55 | return EINVAL; | 81 | return EINVAL; |
56 | 82 | ||
57 | l = malloc (sizeof (*l)); | 83 | l = calloc (1, sizeof (*l)); |
58 | if (l == NULL) | 84 | if (l == NULL) |
59 | return ENOMEM; | 85 | return ENOMEM; |
60 | 86 | ||
61 | l->fname = calloc (len + 5 /*strlen(".lock")*/ + 1, sizeof (*(l->fname))); | 87 | /* Should make l->file be the resulting of following the symlinks. */ |
62 | if (l->fname == NULL) | 88 | l->file = strdup(filename); |
89 | if (l->file == NULL) | ||
63 | { | 90 | { |
64 | free (l); | 91 | free (l); |
65 | return ENOMEM; | 92 | return ENOMEM; |
66 | } | 93 | } |
67 | memcpy (l->fname, filename, len); | 94 | |
68 | strcat (l->fname, ".lock"); | 95 | l->dotlock = malloc(strlen(l->file) + 5 /*strlen(".lock")*/ + 1); |
96 | |||
97 | if(!l->dotlock) | ||
98 | { | ||
99 | free(l->file); | ||
100 | free(l); | ||
101 | return ENOMEM; | ||
102 | } | ||
103 | |||
104 | sprintf(l->dotlock, "%s.lock", l->file); | ||
69 | 105 | ||
70 | if (flags) | 106 | if (flags) |
71 | l->flags = flags; | 107 | l->flags = flags; |
72 | else | 108 | else |
73 | l->flags = MU_LOCKER_TIME; /* Default is time lock implementation. */ | 109 | l->flags = MU_LOCKER_DEFAULT; |
110 | |||
74 | l->fd = -1; | 111 | l->fd = -1; |
75 | l->refcnt = 0; | 112 | l->expire_time = MU_LOCKER_EXPIRE_TIME; |
113 | l->retries = MU_LOCKER_RETRIES; | ||
114 | l->retry_sleep = MU_LOCKER_RETRY_SLEEP; | ||
115 | |||
116 | INVARIANT(l) | ||
117 | |||
76 | *plocker = l; | 118 | *plocker = l; |
119 | |||
77 | return 0; | 120 | return 0; |
78 | } | 121 | } |
79 | 122 | ||
... | @@ -82,151 +125,387 @@ locker_destroy (locker_t *plocker) | ... | @@ -82,151 +125,387 @@ locker_destroy (locker_t *plocker) |
82 | { | 125 | { |
83 | if (plocker && *plocker) | 126 | if (plocker && *plocker) |
84 | { | 127 | { |
85 | free ((*plocker)->fname); | 128 | close((*plocker)->fd); |
129 | free ((*plocker)->file); | ||
130 | free ((*plocker)->dotlock); | ||
86 | free (*plocker); | 131 | free (*plocker); |
87 | *plocker = NULL; | 132 | *plocker = NULL; |
88 | } | 133 | } |
89 | } | 134 | } |
90 | 135 | ||
136 | int locker_set_flags (locker_t locker, int flags) | ||
137 | { | ||
138 | if (!locker) | ||
139 | return MU_ERR_LOCKER_NULL; | ||
140 | |||
141 | locker->flags = flags; | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
91 | int | 146 | int |
92 | locker_lock (locker_t lock, int flags) | 147 | locker_set_expire_time (locker_t locker, int time) |
93 | { | 148 | { |
94 | int fd = -1; | 149 | if (!locker) |
95 | char buf[16]; | 150 | return MU_ERR_LOCKER_NULL; |
96 | pid_t pid; | 151 | |
97 | int removed = 0; | 152 | if(time <= 0) |
153 | return EINVAL; | ||
154 | |||
155 | locker->expire_time = time; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | int | ||
161 | locker_set_retries (locker_t locker, int retries) | ||
162 | { | ||
163 | if (!locker) | ||
164 | return MU_ERR_LOCKER_NULL; | ||
165 | |||
166 | if(retries <= 0) | ||
167 | return EINVAL; | ||
168 | |||
169 | locker->retries = retries; | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | int | ||
175 | locker_set_retry_sleep (locker_t locker, int retry_sleep) | ||
176 | { | ||
177 | if (!locker) | ||
178 | return MU_ERR_LOCKER_NULL; | ||
179 | |||
180 | if(retry_sleep <= 0) | ||
181 | return EINVAL; | ||
182 | |||
183 | locker->retry_sleep = retry_sleep; | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | int locker_get_flags (locker_t locker, int *flags) | ||
189 | { | ||
190 | if (!locker) | ||
191 | return MU_ERR_LOCKER_NULL; | ||
192 | |||
193 | if(!flags) | ||
194 | return EINVAL; | ||
195 | |||
196 | *flags = locker->flags; | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | int | ||
202 | locker_get_expire_time (locker_t locker, int *time) | ||
203 | { | ||
204 | if (!locker) | ||
205 | return MU_ERR_LOCKER_NULL; | ||
206 | |||
207 | if(!time) | ||
208 | return EINVAL; | ||
209 | |||
210 | *time = locker->expire_time; | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | int | ||
216 | locker_get_retries (locker_t locker, int *retries) | ||
217 | { | ||
218 | if (!locker) | ||
219 | return MU_ERR_LOCKER_NULL; | ||
220 | |||
221 | if(!retries) | ||
222 | return EINVAL; | ||
223 | |||
224 | *retries = locker->retries; | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | int | ||
230 | locker_get_retry_sleep (locker_t locker, int *retry_sleep) | ||
231 | { | ||
232 | if (!locker) | ||
233 | return MU_ERR_LOCKER_NULL; | ||
234 | |||
235 | if(!retry_sleep) | ||
236 | return EINVAL; | ||
237 | |||
238 | *retry_sleep = locker->retry_sleep; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */ | ||
244 | static void | ||
245 | expire_stale_lock (locker_t lock) | ||
246 | { | ||
247 | int stale = 0; | ||
248 | int fd = open (lock->dotlock, O_RDONLY); | ||
249 | if (fd == -1) | ||
250 | return; | ||
251 | |||
252 | /* Check to see if this process is still running. */ | ||
253 | if (lock->flags & MU_LOCKER_PID) | ||
254 | { | ||
255 | char buf[16]; | ||
256 | pid_t pid; | ||
257 | int nread = read (fd, buf, sizeof (buf) - 1); | ||
258 | if (nread > 0) | ||
259 | { | ||
260 | buf[nread] = '\0'; | ||
261 | pid = strtol (buf, NULL, 10); | ||
262 | if (pid > 0) | ||
263 | { | ||
264 | /* Process is gone so we try to remove the lock. */ | ||
265 | if (kill (pid, 0) == -1) | ||
266 | stale = 1; | ||
267 | } | ||
268 | else | ||
269 | stale = 1; /* Corrupted file, remove the lock. */ | ||
270 | } | ||
271 | } | ||
272 | /* Check to see if the lock expired. */ | ||
273 | if (lock->flags & MU_LOCKER_TIME) | ||
274 | { | ||
275 | struct stat stbuf; | ||
276 | |||
277 | fstat (fd, &stbuf); | ||
278 | /* The lock has expired. */ | ||
279 | if ((time (NULL) - stbuf.st_mtime) > lock->expire_time) | ||
280 | stale = 1; | ||
281 | } | ||
282 | |||
283 | close (fd); | ||
284 | if (stale) | ||
285 | unlink (lock->dotlock); | ||
286 | } | ||
287 | |||
288 | static int | ||
289 | stat_check (const char *file, int fd) | ||
290 | { | ||
291 | struct stat fn_stat; | ||
292 | struct stat fd_stat; | ||
293 | int err = 0; | ||
294 | |||
295 | /* We should always be able to stat a valid fd, so this | ||
296 | is an error condition. */ | ||
297 | if (lstat (file, &fn_stat) || fstat (fd, &fd_stat)) | ||
298 | err = errno; | ||
299 | else | ||
300 | { | ||
301 | /* If the link and stat don't report the same info, or the | ||
302 | file is a symlink, fail the locking. */ | ||
303 | #define CHK(X) if(X) return EINVAL | ||
304 | |||
305 | CHK (!S_ISREG (fn_stat.st_mode)); | ||
306 | CHK (!S_ISREG (fd_stat.st_mode)); | ||
307 | CHK (fn_stat.st_nlink != 1); | ||
308 | CHK (fn_stat.st_dev != fd_stat.st_dev); | ||
309 | CHK (fn_stat.st_ino != fd_stat.st_ino); | ||
310 | CHK (fn_stat.st_mode != fd_stat.st_mode); | ||
311 | CHK (fn_stat.st_nlink != fd_stat.st_nlink); | ||
312 | CHK (fn_stat.st_uid != fd_stat.st_uid); | ||
313 | CHK (fn_stat.st_gid != fd_stat.st_gid); | ||
314 | CHK (fn_stat.st_rdev != fd_stat.st_rdev); | ||
315 | |||
316 | #undef CHK | ||
317 | } | ||
318 | return err; | ||
319 | } | ||
320 | |||
321 | int | ||
322 | locker_lock (locker_t lock) | ||
323 | { | ||
324 | int err = 0; | ||
325 | int fd = -1; | ||
326 | int retries = 1; | ||
98 | 327 | ||
99 | (void)flags; /* Ignore for now. */ | ||
100 | if (lock == NULL) | 328 | if (lock == NULL) |
101 | return EINVAL; | 329 | return EINVAL; |
102 | 330 | ||
103 | /* Is the lock already applied? | 331 | INVARIANT (lock) |
104 | FIXME: should we check flags != lock->flags ?? */ | 332 | /* Is the lock already applied? */ |
105 | if (lock->fd != -1) | 333 | if (lock->refcnt > 0) |
106 | { | 334 | { |
335 | assert (lock->fd != -1); | ||
107 | lock->refcnt++; | 336 | lock->refcnt++; |
108 | return 0; | 337 | return 0; |
109 | } | 338 | } |
110 | 339 | ||
111 | /* | 340 | assert (lock->fd == -1); |
112 | Check for lock existance: | ||
113 | if it exists but the process is gone the lock can be removed, | ||
114 | if the lock is expired, remove it. */ | ||
115 | fd = open (lock->fname, O_RDONLY); | ||
116 | if (fd != -1) | ||
117 | { | ||
118 | /* Check to see if this process is still running. */ | ||
119 | if (lock->flags & MU_LOCKER_PID) | ||
120 | { | ||
121 | int nread = read (fd, buf, sizeof (buf) - 1); | ||
122 | if (nread > 0) | ||
123 | { | ||
124 | buf[nread] = '\0'; | ||
125 | pid = strtol (buf, NULL, 10); | ||
126 | if (pid > 0) | ||
127 | { | ||
128 | /* Process is gone so we try to remove the lock. */ | ||
129 | if (kill (pid, 0) == -1) | ||
130 | removed = 1; | ||
131 | } | ||
132 | else | ||
133 | removed = 1; /* Corrupted file, remove the lock. */ | ||
134 | } | ||
135 | } | ||
136 | /* Check to see if the lock expired. */ | ||
137 | if (lock->flags & MU_LOCKER_TIME) | ||
138 | { | ||
139 | struct stat stbuf; | ||
140 | |||
141 | fstat (fd, &stbuf); | ||
142 | /* The lock has expired. */ | ||
143 | if ((time (NULL) - stbuf.st_mtime) > MU_LOCKER_EXPIRE_TIME) | ||
144 | removed = 1; | ||
145 | } | ||
146 | 341 | ||
342 | /* Check we are trying to lock a regular file, with a link count | ||
343 | of 1, that we have permission to read, etc., or don't lock it. */ | ||
344 | if ((fd = open (lock->file, O_RDONLY)) == -1) | ||
345 | { | ||
346 | return errno; | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | err = stat_check (lock->file, fd); | ||
147 | close (fd); | 351 | close (fd); |
148 | if (removed) | 352 | fd = -1; |
149 | unlink (lock->fname); | 353 | if (err) |
354 | { | ||
355 | if (err == EINVAL) | ||
356 | err = MU_ERR_LOCK_BAD_FILE; | ||
357 | return err; | ||
358 | } | ||
150 | } | 359 | } |
151 | 360 | ||
152 | /* Try to create the lockfile. */ | 361 | if (lock->flags & MU_LOCKER_RETRY) |
153 | fd = open (lock->fname, O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); | 362 | { |
154 | if (fd == -1) | 363 | retries = lock->retries; |
155 | return errno; | 364 | } |
156 | else | 365 | |
366 | while (retries--) | ||
157 | { | 367 | { |
158 | struct stat fn_stat; | 368 | /* cleanup after last loop */ |
159 | struct stat fd_stat; | 369 | close (fd); |
160 | 370 | fd = -1; | |
161 | if (lstat (lock->fname, &fn_stat) | 371 | |
162 | || fstat(fd, &fd_stat) | 372 | expire_stale_lock (lock); |
163 | || fn_stat.st_nlink != 1 | 373 | |
164 | || fn_stat.st_dev != fd_stat.st_dev | 374 | /* Try to create the lockfile. */ |
165 | || fn_stat.st_ino != fd_stat.st_ino | 375 | fd = open (lock->dotlock, O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); |
166 | || fn_stat.st_uid != fd_stat.st_uid | 376 | if (fd == -1) |
167 | || fn_stat.st_gid != fd_stat.st_gid) | ||
168 | { | 377 | { |
169 | close (fd); | 378 | err = errno; |
170 | unlink (lock->fname); | 379 | |
171 | return EPERM; | 380 | /* EEXIST means somebody else has the lock, anything else is an |
381 | error. */ | ||
382 | if (err == EEXIST) | ||
383 | err = MU_ERR_LOCK_CONFLICT; | ||
384 | else | ||
385 | return err; | ||
172 | } | 386 | } |
387 | else | ||
388 | { | ||
389 | err = stat_check (lock->dotlock, fd); | ||
390 | if (err == 0) | ||
391 | { | ||
392 | /* We got the lock! */ | ||
393 | break; | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | close (fd); | ||
398 | unlink (lock->dotlock); | ||
399 | |||
400 | /* If there was something invalid about the file/fd, return | ||
401 | a more descriptive code. */ | ||
402 | if (err == EINVAL) | ||
403 | err = MU_ERR_LOCK_BAD_LOCK; | ||
404 | |||
405 | return err; | ||
406 | } | ||
407 | } | ||
408 | if(retries) | ||
409 | sleep(lock->retry_sleep); | ||
173 | } | 410 | } |
174 | 411 | ||
175 | /* Success. */ | 412 | if (err) |
176 | sprintf (buf, "%ld", (long)getpid ()); | 413 | return err; |
177 | write (fd, buf, strlen (buf)); | 414 | |
415 | /* We have the lock. */ | ||
416 | assert (lock->refcnt == 0); | ||
178 | 417 | ||
179 | /* Try to get a file lock. */ | 418 | if (lock->flags & MU_LOCKER_PID) |
180 | if (lock->flags & MU_LOCKER_FCNTL) | ||
181 | { | 419 | { |
182 | struct flock fl; | 420 | char buf[16]; |
183 | 421 | sprintf (buf, "%ld", (long) getpid ()); | |
184 | memset (&fl, 0, sizeof (struct flock)); | 422 | write (fd, buf, strlen (buf)); |
185 | fl.l_type = F_WRLCK; | ||
186 | if (fcntl (fd, F_SETLK, &fl) == -1) | ||
187 | { | ||
188 | int err = errno; | ||
189 | /* Could not get the file lock. */ | ||
190 | close (fd); | ||
191 | unlink (lock->fname); /* Remove the file I created. */ | ||
192 | return err; | ||
193 | } | ||
194 | } | 423 | } |
195 | 424 | ||
425 | lock->refcnt = 1; | ||
196 | lock->fd = fd; | 426 | lock->fd = fd; |
197 | lock->refcnt++; | 427 | |
428 | INVARIANT (lock); | ||
429 | |||
198 | return 0; | 430 | return 0; |
199 | } | 431 | } |
200 | 432 | ||
201 | int | 433 | int |
202 | locker_touchlock (locker_t lock) | 434 | locker_touchlock (locker_t lock) |
203 | { | 435 | { |
204 | if (!lock || !lock->fname || lock->fd == -1) | 436 | if (!lock) |
205 | return EINVAL; | 437 | return MU_ERR_LOCKER_NULL; |
206 | return utime (lock->fname, NULL); | 438 | |
439 | assert(lock->dotlock); | ||
440 | |||
441 | INVARIANT(lock); | ||
442 | |||
443 | if(lock->refcnt > 0) | ||
444 | return utime (lock->dotlock, NULL); | ||
445 | |||
446 | return MU_ERR_LOCK_NOT_HELD; | ||
207 | } | 447 | } |
208 | 448 | ||
209 | int | 449 | int |
210 | locker_unlock (locker_t lock) | 450 | locker_unlock (locker_t lock) |
211 | { | 451 | { |
212 | if (!lock || !lock->fname || lock->fd == -1 || lock->refcnt <= 0) | 452 | assert(lock->refcnt >= 0); |
213 | return EINVAL; | 453 | |
454 | /* FIXME: distinguish between bad args and lock not held */ | ||
455 | if (!lock) | ||
456 | return MU_ERR_LOCKER_NULL; | ||
457 | |||
458 | INVARIANT(lock); | ||
459 | |||
460 | if(lock->refcnt == 0) | ||
461 | return MU_ERR_LOCK_NOT_HELD; | ||
462 | |||
463 | assert(lock->fd != -1); | ||
214 | 464 | ||
215 | if (--lock->refcnt > 0) | 465 | if (--lock->refcnt > 0) |
216 | return 0; | 466 | return 0; |
217 | 467 | ||
218 | if (lock->flags & MU_LOCKER_FCNTL) | 468 | assert(lock->refcnt == 0); |
219 | { | ||
220 | struct flock fl; | ||
221 | 469 | ||
222 | memset (&fl, 0, sizeof (struct flock)); | ||
223 | fl.l_type = F_UNLCK; | ||
224 | /* Unlock failed? */ | ||
225 | if (fcntl (lock->fd, F_SETLK, &fl) == -1) | ||
226 | return errno; | ||
227 | } | ||
228 | close (lock->fd); | 470 | close (lock->fd); |
229 | lock->fd = -1; | 471 | lock->fd = -1; |
230 | unlink (lock->fname); | 472 | unlink (lock->dotlock); |
473 | |||
474 | INVARIANT(lock); | ||
475 | |||
231 | return 0; | 476 | return 0; |
232 | } | 477 | } |
478 | int | ||
479 | locker_remove_lock (locker_t lock) | ||
480 | { | ||
481 | int err; | ||
482 | |||
483 | if (!lock) | ||
484 | return MU_ERR_LOCKER_NULL; | ||
485 | |||
486 | INVARIANT(lock); | ||
487 | |||
488 | /* If we hold the lock, do an unlock... */ | ||
489 | if(lock->refcnt > 0) | ||
490 | { | ||
491 | /* Force the reference count to 1 to unlock the file. */ | ||
492 | lock->refcnt = 1; | ||
493 | return locker_unlock(lock); | ||
494 | } | ||
495 | |||
496 | /* ... if we don't, unlink the lockfile. */ | ||
497 | err = unlink (lock->dotlock); | ||
498 | |||
499 | if(err == -1) | ||
500 | { | ||
501 | err = errno; | ||
502 | |||
503 | if(err == ENOENT) | ||
504 | err = MU_ERR_LOCK_NOT_HELD; | ||
505 | } | ||
506 | |||
507 | INVARIANT(lock); | ||
508 | |||
509 | return err; | ||
510 | } | ||
511 | ... | ... |
-
Please register or sign in to post a comment