/src/gnupg/common/dotlock.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* dotlock.c - dotfile locking |
2 | | * Copyright (C) 1998, 2000, 2001, 2003, 2004, |
3 | | * 2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc. |
4 | | * |
5 | | * This file is part of GnuPG. |
6 | | * |
7 | | * GnuPG is free software; you can redistribute and/or modify this |
8 | | * part of GnuPG under the terms of either |
9 | | * |
10 | | * - the GNU Lesser General Public License as published by the Free |
11 | | * Software Foundation; either version 3 of the License, or (at |
12 | | * your option) any later version. |
13 | | * |
14 | | * or |
15 | | * |
16 | | * - the GNU General Public License as published by the Free |
17 | | * Software Foundation; either version 2 of the License, or (at |
18 | | * your option) any later version. |
19 | | * |
20 | | * or both in parallel, as here. |
21 | | * |
22 | | * GnuPG is distributed in the hope that it will be useful, but |
23 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
24 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
25 | | * General Public License for more details. |
26 | | * |
27 | | * You should have received a copies of the GNU General Public License |
28 | | * and the GNU Lesser General Public License along with this program; |
29 | | * if not, see <https://www.gnu.org/licenses/>. |
30 | | * |
31 | | * ALTERNATIVELY, this file may be distributed under the terms of the |
32 | | * following license, in which case the provisions of this license are |
33 | | * required INSTEAD OF the GNU Lesser General License or the GNU |
34 | | * General Public License. If you wish to allow use of your version of |
35 | | * this file only under the terms of the GNU Lesser General License or |
36 | | * the GNU General Public License, and not to allow others to use your |
37 | | * version of this file under the terms of the following license, |
38 | | * indicate your decision by deleting this paragraph and the license |
39 | | * below. |
40 | | * |
41 | | * Redistribution and use in source and binary forms, with or without |
42 | | * modification, are permitted provided that the following conditions |
43 | | * are met: |
44 | | * |
45 | | * 1. Redistributions of source code must retain the above copyright |
46 | | * notice, and the entire permission notice in its entirety, |
47 | | * including the disclaimer of warranties. |
48 | | * 2. Redistributions in binary form must reproduce the above copyright |
49 | | * notice, this list of conditions and the following disclaimer in the |
50 | | * documentation and/or other materials provided with the distribution. |
51 | | * 3. The name of the author may not be used to endorse or promote |
52 | | * products derived from this software without specific prior |
53 | | * written permission. |
54 | | * |
55 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
56 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
57 | | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
58 | | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
59 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
60 | | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
61 | | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
62 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
63 | | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
64 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
65 | | * OF THE POSSIBILITY OF SUCH DAMAGE. |
66 | | */ |
67 | | |
68 | | /* |
69 | | Overview: |
70 | | ========= |
71 | | |
72 | | This module implements advisory file locking in a portable way. |
73 | | Due to the problems with POSIX fcntl locking a separate lock file |
74 | | is used. It would be possible to use fcntl locking on this lock |
75 | | file and thus avoid the weird auto unlock bug of POSIX while still |
76 | | having an unproved better performance of fcntl locking. However |
77 | | there are still problems left, thus we resort to use a hardlink |
78 | | which has the well defined property that a link call will fail if |
79 | | the target file already exists. |
80 | | |
81 | | Given that hardlinks are also available on NTFS file systems since |
82 | | Windows XP; it will be possible to enhance this module to use |
83 | | hardlinks even on Windows and thus allow Windows and Posix clients |
84 | | to use locking on the same directory. This is not yet implemented; |
85 | | instead we use a lockfile on Windows along with W32 style file |
86 | | locking. |
87 | | |
88 | | On FAT file systems hardlinks are not supported. Thus this method |
89 | | does not work. Our solution is to use a O_EXCL locking instead. |
90 | | Querying the type of the file system is not easy to do in a |
91 | | portable way (e.g. Linux has a statfs, BSDs have a the same call |
92 | | but using different structures and constants). What we do instead |
93 | | is to check at runtime whether link(2) works for a specific lock |
94 | | file. |
95 | | |
96 | | |
97 | | How to use: |
98 | | =========== |
99 | | |
100 | | At program initialization time, the module should be explicitly |
101 | | initialized: |
102 | | |
103 | | dotlock_create (NULL, 0); |
104 | | |
105 | | This installs an atexit handler and may also initialize mutex etc. |
106 | | It is optional for non-threaded applications. Only the first call |
107 | | has an effect. This needs to be done before any extra threads are |
108 | | started. |
109 | | |
110 | | To create a lock file (which prepares it but does not take the |
111 | | lock) you do: |
112 | | |
113 | | dotlock_t h |
114 | | |
115 | | h = dotlock_create (fname, 0); |
116 | | if (!h) |
117 | | error ("error creating lock file: %s\n", strerror (errno)); |
118 | | |
119 | | It is important to handle the error. For example on a read-only |
120 | | file system a lock can't be created (but is usually not needed). |
121 | | FNAME is the file you want to lock; the actual lockfile is that |
122 | | name with the suffix ".lock" appended. On success a handle to be |
123 | | used with the other functions is returned or NULL on error. Note |
124 | | that the handle shall only be used by one thread at a time. This |
125 | | function creates a unique file temporary file (".#lk*") in the same |
126 | | directory as FNAME and returns a handle for further operations. |
127 | | The module keeps track of these unique files so that they will be |
128 | | unlinked using the atexit handler. If you don't need the lock file |
129 | | anymore, you may also explicitly remove it with a call to: |
130 | | |
131 | | dotlock_destroy (h); |
132 | | |
133 | | To actually lock the file, you use: |
134 | | |
135 | | if (dotlock_take (h, -1)) |
136 | | error ("error taking lock: %s\n", strerror (errno)); |
137 | | |
138 | | This function will wait until the lock is acquired. If an |
139 | | unexpected error occurs if will return non-zero and set ERRNO. If |
140 | | you pass (0) instead of (-1) the function does not wait in case the |
141 | | file is already locked but returns -1 and sets ERRNO to EACCES. |
142 | | Any other positive value for the second parameter is considered a |
143 | | timeout value in milliseconds. |
144 | | |
145 | | To release the lock you call: |
146 | | |
147 | | if (dotlock_release (h)) |
148 | | error ("error releasing lock: %s\n", strerror (errno)); |
149 | | |
150 | | or, if the lock file is not anymore needed, you may just call |
151 | | dotlock_destroy. However dotlock_release does some extra checks |
152 | | before releasing the lock and prints diagnostics to help detecting |
153 | | bugs. |
154 | | |
155 | | If you want to explicitly destroy all lock files you may call |
156 | | |
157 | | dotlock_remove_lockfiles (); |
158 | | |
159 | | which is the core of the installed atexit handler. In case your |
160 | | application wants to disable locking completely it may call |
161 | | |
162 | | disable_locking () |
163 | | |
164 | | before any locks are created. |
165 | | |
166 | | There are two convenience functions to store an integer (e.g. a |
167 | | file descriptor) value with the handle: |
168 | | |
169 | | void dotlock_set_fd (dotlock_t h, int fd); |
170 | | int dotlock_get_fd (dotlock_t h); |
171 | | |
172 | | If nothing has been stored dotlock_get_fd returns -1. |
173 | | |
174 | | |
175 | | |
176 | | How to build: |
177 | | ============= |
178 | | |
179 | | This module was originally developed for GnuPG but later changed to |
180 | | allow its use without any GnuPG dependency. If you want to use it |
181 | | with you application you may simply use it and it should figure out |
182 | | most things automagically. |
183 | | |
184 | | You may use the common config.h file to pass macros, but take care |
185 | | to pass -DHAVE_CONFIG_H to the compiler. Macros used by this |
186 | | module are: |
187 | | |
188 | | DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use. |
189 | | |
190 | | DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions. |
191 | | |
192 | | DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the |
193 | | string to which this macro evaluates. |
194 | | |
195 | | GNUPG_MAJOR_VERSION - Defined when used by GnuPG. |
196 | | |
197 | | HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be |
198 | | automatically defined if a the target is |
199 | | Windows. |
200 | | |
201 | | HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM. |
202 | | |
203 | | HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h |
204 | | is not used defaults to defined. |
205 | | |
206 | | DIRSEP_C - Separation character for file name parts. |
207 | | Usually not redefined. |
208 | | |
209 | | EXTSEP_S - Separation string for file name suffixes. |
210 | | Usually not redefined. |
211 | | |
212 | | Note that there is a test program t-dotlock which has compile |
213 | | instructions at its end. At least for SMBFS and CIFS it is |
214 | | important that 64 bit versions of stat are used; most programming |
215 | | environments do this these days, just in case you want to compile |
216 | | it on the command line, remember to pass -D_FILE_OFFSET_BITS=64 |
217 | | |
218 | | |
219 | | Bugs: |
220 | | ===== |
221 | | |
222 | | On Windows this module is not yet thread-safe. |
223 | | |
224 | | |
225 | | Miscellaneous notes: |
226 | | ==================== |
227 | | |
228 | | On hardlinks: |
229 | | - Hardlinks are supported under Windows with NTFS since XP/Server2003. |
230 | | - In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks. |
231 | | - NFS supports hard links. But there are solvable problems. |
232 | | - FAT does not support links |
233 | | |
234 | | On the file locking API: |
235 | | - CIFS on Linux 2.6.33 supports several locking methods. |
236 | | SMBFS seems not to support locking. No closer checks done. |
237 | | - NFS supports Posix locks. flock is emulated in the server. |
238 | | However there are a couple of problems; see below. |
239 | | - FAT does not support locks. |
240 | | - An advantage of fcntl locking is that R/W locks can be |
241 | | implemented which is not easy with a straight lock file. |
242 | | |
243 | | On O_EXCL: |
244 | | - Does not work reliable on NFS |
245 | | - Should work on CIFS and SMBFS but how can we delete lockfiles? |
246 | | |
247 | | On NFS problems: |
248 | | - Locks vanish if the server crashes and reboots. |
249 | | - Client crashes keep the lock in the server until the client |
250 | | re-connects. |
251 | | - Communication problems may return unreliable error codes. The |
252 | | MUA Postfix's workaround is to compare the link count after |
253 | | seeing an error for link. However that gives a race. If using a |
254 | | unique file to link to a lockfile and using stat to check the |
255 | | link count instead of looking at the error return of link(2) is |
256 | | the best solution. |
257 | | - O_EXCL seems to have a race and may re-create a file anyway. |
258 | | |
259 | | */ |
260 | | |
261 | | #ifdef HAVE_CONFIG_H |
262 | | # include <config.h> |
263 | | #endif |
264 | | |
265 | | /* Some quick replacements for stuff we usually expect to be defined |
266 | | in config.h. Define HAVE_POSIX_SYSTEM for better readability. */ |
267 | | #if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32) |
268 | | # define HAVE_DOSISH_SYSTEM 1 |
269 | | #endif |
270 | | #if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM) |
271 | | # define HAVE_POSIX_SYSTEM 1 |
272 | | #endif |
273 | | |
274 | | /* With no config.h assume that we have sitgnal.h. */ |
275 | | #if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM) |
276 | | # define HAVE_SIGNAL_H 1 |
277 | | #endif |
278 | | |
279 | | /* Standard headers. */ |
280 | | #include <stdlib.h> |
281 | | #include <stdio.h> |
282 | | #include <string.h> |
283 | | #include <errno.h> |
284 | | #include <ctype.h> |
285 | | #include <errno.h> |
286 | | #include <unistd.h> |
287 | | #ifdef HAVE_DOSISH_SYSTEM |
288 | | # define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ |
289 | | # include <windows.h> |
290 | | #else |
291 | | # include <sys/types.h> |
292 | | # include <sys/stat.h> |
293 | | # include <sys/utsname.h> |
294 | | #endif |
295 | | #include <sys/types.h> |
296 | | #include <sys/time.h> |
297 | | #include <sys/stat.h> |
298 | | #include <fcntl.h> |
299 | | #ifdef HAVE_SIGNAL_H |
300 | | # include <signal.h> |
301 | | #endif |
302 | | #ifdef DOTLOCK_USE_PTHREAD |
303 | | # include <pthread.h> |
304 | | #endif |
305 | | |
306 | | #ifdef DOTLOCK_GLIB_LOGGING |
307 | | # include <glib.h> |
308 | | #endif |
309 | | |
310 | | #ifdef GNUPG_MAJOR_VERSION |
311 | | # include "util.h" |
312 | | # include "common-defs.h" |
313 | | # include "stringhelp.h" /* For stpcpy and w32_strerror. */ |
314 | | #endif |
315 | | |
316 | | #include "dotlock.h" |
317 | | |
318 | | |
319 | | /* Define constants for file name construction. */ |
320 | | #if !defined(DIRSEP_C) && !defined(EXTSEP_S) |
321 | | # ifdef HAVE_DOSISH_SYSTEM |
322 | | # define DIRSEP_C '\\' |
323 | | # define EXTSEP_S "." |
324 | | #else |
325 | | # define DIRSEP_C '/' |
326 | | # define EXTSEP_S "." |
327 | | # endif |
328 | | #endif |
329 | | |
330 | | /* In GnuPG we use wrappers around the malloc functions. If they are |
331 | | not defined we assume that this code is used outside of GnuPG and |
332 | | fall back to the regular malloc functions. */ |
333 | | #ifndef xtrymalloc |
334 | | # define xtrymalloc(a) malloc ((a)) |
335 | | # define xtrycalloc(a,b) calloc ((a), (b)) |
336 | | # define xfree(a) free ((a)) |
337 | | #endif |
338 | | |
339 | | /* Wrapper to set ERRNO (required for W32CE). */ |
340 | | #ifdef GPG_ERROR_VERSION |
341 | 3 | # define my_set_errno(e) gpg_err_set_errno ((e)) |
342 | | #else |
343 | | # define my_set_errno(e) do { errno = (e); } while (0) |
344 | | #endif |
345 | | |
346 | | /* Gettext macro replacement. */ |
347 | | #ifndef _ |
348 | | # define _(a) (a) |
349 | | #endif |
350 | | |
351 | | #ifdef GNUPG_MAJOR_VERSION |
352 | 0 | # define my_info_0(a) log_info ((a)) |
353 | 0 | # define my_info_1(a,b) log_info ((a), (b)) |
354 | 0 | # define my_info_2(a,b,c) log_info ((a), (b), (c)) |
355 | 0 | # define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d)) |
356 | 0 | # define my_error_0(a) log_error ((a)) |
357 | 0 | # define my_error_1(a,b) log_error ((a), (b)) |
358 | 0 | # define my_error_2(a,b,c) log_error ((a), (b), (c)) |
359 | 0 | # define my_debug_1(a,b) log_debug ((a), (b)) |
360 | | # define my_fatal_0(a) log_fatal ((a)) |
361 | | #elif defined (DOTLOCK_GLIB_LOGGING) |
362 | | # define my_info_0(a) g_message ((a)) |
363 | | # define my_info_1(a,b) g_message ((a), (b)) |
364 | | # define my_info_2(a,b,c) g_message ((a), (b), (c)) |
365 | | # define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d)) |
366 | | # define my_error_0(a) g_warning ((a)) |
367 | | # define my_error_1(a,b) g_warning ((a), (b)) |
368 | | # define my_error_2(a,b,c) g_warning ((a), (b), (c)) |
369 | | # define my_debug_1(a,b) g_debug ((a), (b)) |
370 | | # define my_fatal_0(a) g_error ((a)) |
371 | | #else |
372 | | # define my_info_0(a) fprintf (stderr, (a)) |
373 | | # define my_info_1(a,b) fprintf (stderr, (a), (b)) |
374 | | # define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c)) |
375 | | # define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d)) |
376 | | # define my_error_0(a) fprintf (stderr, (a)) |
377 | | # define my_error_1(a,b) fprintf (stderr, (a), (b)) |
378 | | # define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c)) |
379 | | # define my_debug_1(a,b) fprintf (stderr, (a), (b)) |
380 | | # define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \ |
381 | | abort (); } while (0) |
382 | | #endif |
383 | | |
384 | | |
385 | | |
386 | | |
387 | | |
388 | | /* The object describing a lock. */ |
389 | | struct dotlock_handle |
390 | | { |
391 | | struct dotlock_handle *next; |
392 | | char *lockname; /* Name of the actual lockfile. */ |
393 | | unsigned int locked:1; /* Lock status. */ |
394 | | unsigned int disable:1; /* If true, locking is disabled. */ |
395 | | unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */ |
396 | | |
397 | | int extra_fd; /* A place for the caller to store an FD. */ |
398 | | |
399 | | #ifdef HAVE_DOSISH_SYSTEM |
400 | | HANDLE lockhd; /* The W32 handle of the lock file. */ |
401 | | #else /*!HAVE_DOSISH_SYSTEM */ |
402 | | char *tname; /* Name of the lockfile template. */ |
403 | | size_t nodename_off; /* Offset in TNAME of the nodename part. */ |
404 | | size_t nodename_len; /* Length of the nodename part. */ |
405 | | #endif /*!HAVE_DOSISH_SYSTEM */ |
406 | | }; |
407 | | |
408 | | |
409 | | /* A list of all lock handles. The volatile attribute might help |
410 | | if used in an atexit handler. Note that [UN]LOCK_all_lockfiles |
411 | | must not change ERRNO. */ |
412 | | static volatile dotlock_t all_lockfiles; |
413 | | #ifdef DOTLOCK_USE_PTHREAD |
414 | | static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER; |
415 | | # define LOCK_all_lockfiles() do { \ |
416 | | if (pthread_mutex_lock (&all_lockfiles_mutex)) \ |
417 | | my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \ |
418 | | } while (0) |
419 | | # define UNLOCK_all_lockfiles() do { \ |
420 | | if (pthread_mutex_unlock (&all_lockfiles_mutex)) \ |
421 | | my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \ |
422 | | } while (0) |
423 | | #else /*!DOTLOCK_USE_PTHREAD*/ |
424 | 10 | # define LOCK_all_lockfiles() do { } while (0) |
425 | 10 | # define UNLOCK_all_lockfiles() do { } while (0) |
426 | | #endif /*!DOTLOCK_USE_PTHREAD*/ |
427 | | |
428 | | /* If this has the value true all locking is disabled. */ |
429 | | static int never_lock; |
430 | | |
431 | | |
432 | | |
433 | | |
434 | | #ifdef HAVE_DOSISH_SYSTEM |
435 | | /* FIXME: For use in GnuPG this can be replaced by |
436 | | * gnupg_w32_set_errno. */ |
437 | | static int |
438 | | map_w32_to_errno (DWORD w32_err) |
439 | | { |
440 | | switch (w32_err) |
441 | | { |
442 | | case 0: |
443 | | return 0; |
444 | | |
445 | | case ERROR_FILE_NOT_FOUND: |
446 | | return ENOENT; |
447 | | |
448 | | case ERROR_PATH_NOT_FOUND: |
449 | | return ENOENT; |
450 | | |
451 | | case ERROR_ACCESS_DENIED: |
452 | | return EPERM; |
453 | | |
454 | | case ERROR_INVALID_HANDLE: |
455 | | case ERROR_INVALID_BLOCK: |
456 | | return EINVAL; |
457 | | |
458 | | case ERROR_NOT_ENOUGH_MEMORY: |
459 | | return ENOMEM; |
460 | | |
461 | | case ERROR_NO_DATA: |
462 | | case ERROR_BROKEN_PIPE: |
463 | | return EPIPE; |
464 | | |
465 | | default: |
466 | | return EIO; |
467 | | } |
468 | | } |
469 | | #endif /*HAVE_DOSISH_SYSTEM*/ |
470 | | |
471 | | |
472 | | #ifdef HAVE_W32_SYSTEM |
473 | | static int |
474 | | any8bitchar (const char *string) |
475 | | { |
476 | | if (string) |
477 | | for ( ; *string; string++) |
478 | | if ((*string & 0x80)) |
479 | | return 1; |
480 | | return 0; |
481 | | } |
482 | | #endif /*HAVE_W32_SYSTEM*/ |
483 | | |
484 | | |
485 | | |
486 | | |
487 | | /* Entirely disable all locking. This function should be called |
488 | | before any locking is done. It may be called right at startup of |
489 | | the process as it only sets a global value. */ |
490 | | void |
491 | | dotlock_disable (void) |
492 | 0 | { |
493 | 0 | never_lock = 1; |
494 | 0 | } |
495 | | |
496 | | |
497 | | #ifdef HAVE_POSIX_SYSTEM |
498 | | static int |
499 | | maybe_deadlock (dotlock_t h) |
500 | 0 | { |
501 | 0 | dotlock_t r; |
502 | 0 | int res = 0; |
503 | |
|
504 | 0 | LOCK_all_lockfiles (); |
505 | 0 | for (r=all_lockfiles; r; r = r->next) |
506 | 0 | { |
507 | 0 | if ( r != h && r->locked ) |
508 | 0 | { |
509 | 0 | res = 1; |
510 | 0 | break; |
511 | 0 | } |
512 | 0 | } |
513 | 0 | UNLOCK_all_lockfiles (); |
514 | 0 | return res; |
515 | 0 | } |
516 | | #endif /*HAVE_POSIX_SYSTEM*/ |
517 | | |
518 | | |
519 | | /* Read the lock file and return the pid, returns -1 on error. True |
520 | | will be stored in the integer at address SAME_NODE if the lock file |
521 | | has been created on the same node. */ |
522 | | #ifdef HAVE_POSIX_SYSTEM |
523 | | static int |
524 | | read_lockfile (dotlock_t h, int *same_node, int *r_fd) |
525 | 3 | { |
526 | 3 | char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node |
527 | | names are usually shorter. */ |
528 | 3 | int fd; |
529 | 3 | int pid = -1; |
530 | 3 | char *buffer, *p; |
531 | 3 | size_t expected_len; |
532 | 3 | int res, nread; |
533 | | |
534 | 3 | *same_node = 0; |
535 | 3 | expected_len = 10 + 1 + h->nodename_len + 1; |
536 | 3 | if ( expected_len >= sizeof buffer_space) |
537 | 0 | { |
538 | 0 | buffer = xtrymalloc (expected_len); |
539 | 0 | if (!buffer) |
540 | 0 | return -1; |
541 | 0 | } |
542 | 3 | else |
543 | 3 | buffer = buffer_space; |
544 | | |
545 | 3 | if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) |
546 | 0 | { |
547 | 0 | int e = errno; |
548 | 0 | my_info_2 ("error opening lockfile '%s': %s\n", |
549 | 0 | h->lockname, strerror(errno) ); |
550 | 0 | if (buffer != buffer_space) |
551 | 0 | xfree (buffer); |
552 | 0 | my_set_errno (e); /* Need to return ERRNO here. */ |
553 | 0 | return -1; |
554 | 0 | } |
555 | | |
556 | 3 | p = buffer; |
557 | 3 | nread = 0; |
558 | 3 | do |
559 | 3 | { |
560 | 3 | res = read (fd, p, expected_len - nread); |
561 | 3 | if (res == -1 && errno == EINTR) |
562 | 0 | continue; |
563 | 3 | if (res < 0) |
564 | 0 | { |
565 | 0 | int e = errno; |
566 | 0 | my_info_1 ("error reading lockfile '%s'\n", h->lockname ); |
567 | 0 | close (fd); |
568 | 0 | if (buffer != buffer_space) |
569 | 0 | xfree (buffer); |
570 | 0 | my_set_errno (e); |
571 | 0 | return -1; |
572 | 0 | } |
573 | 3 | p += res; |
574 | 3 | nread += res; |
575 | 3 | } |
576 | 3 | while (res && nread != expected_len); |
577 | | |
578 | 3 | if (r_fd) |
579 | 0 | *r_fd = fd; |
580 | 3 | else |
581 | 3 | close(fd); |
582 | | |
583 | 3 | if (nread < 11) |
584 | 0 | { |
585 | 0 | my_info_1 ("invalid size of lockfile '%s'\n", h->lockname); |
586 | 0 | if (buffer != buffer_space) |
587 | 0 | xfree (buffer); |
588 | 0 | my_set_errno (EINVAL); |
589 | 0 | return -1; |
590 | 0 | } |
591 | | |
592 | 3 | if (buffer[10] != '\n' |
593 | 3 | || (buffer[10] = 0, pid = atoi (buffer)) == -1 |
594 | 3 | || !pid ) |
595 | 0 | { |
596 | 0 | my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname); |
597 | 0 | if (buffer != buffer_space) |
598 | 0 | xfree (buffer); |
599 | 0 | my_set_errno (EINVAL); |
600 | 0 | return -1; |
601 | 0 | } |
602 | | |
603 | 3 | if (nread == expected_len |
604 | 3 | && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) |
605 | 3 | && buffer[11+h->nodename_len] == '\n') |
606 | 3 | *same_node = 1; |
607 | | |
608 | 3 | if (buffer != buffer_space) |
609 | 0 | xfree (buffer); |
610 | 3 | return pid; |
611 | 3 | } |
612 | | #endif /*HAVE_POSIX_SYSTEM */ |
613 | | |
614 | | |
615 | | /* Check whether the file system which stores TNAME supports |
616 | | hardlinks. Instead of using the non-portable statsfs call which |
617 | | differs between various Unix versions, we do a runtime test. |
618 | | Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown |
619 | | (test error). */ |
620 | | #ifdef HAVE_POSIX_SYSTEM |
621 | | static int |
622 | | use_hardlinks_p (const char *tname) |
623 | 3 | { |
624 | 3 | char *lname; |
625 | 3 | struct stat sb; |
626 | 3 | unsigned int nlink; |
627 | 3 | int res; |
628 | | |
629 | 3 | if (stat (tname, &sb)) |
630 | 0 | return -1; |
631 | 3 | nlink = (unsigned int)sb.st_nlink; |
632 | | |
633 | 3 | lname = xtrymalloc (strlen (tname) + 1 + 1); |
634 | 3 | if (!lname) |
635 | 0 | return -1; |
636 | 3 | strcpy (lname, tname); |
637 | 3 | strcat (lname, "x"); |
638 | | |
639 | | /* We ignore the return value of link() because it is unreliable. */ |
640 | 3 | (void) link (tname, lname); |
641 | | |
642 | 3 | if (stat (tname, &sb)) |
643 | 0 | res = -1; /* Ooops. */ |
644 | 3 | else if (sb.st_nlink == nlink + 1) |
645 | 3 | res = 0; /* Yeah, hardlinks are supported. */ |
646 | 0 | else |
647 | 0 | res = 1; /* No hardlink support. */ |
648 | | |
649 | 3 | unlink (lname); |
650 | 3 | xfree (lname); |
651 | 3 | return res; |
652 | 3 | } |
653 | | #endif /*HAVE_POSIX_SYSTEM */ |
654 | | |
655 | | |
656 | | |
657 | | #ifdef HAVE_POSIX_SYSTEM |
658 | | /* Locking core for Unix. It used a temporary file and the link |
659 | | system call to make locking an atomic operation. */ |
660 | | static dotlock_t |
661 | | dotlock_create_unix (dotlock_t h, const char *file_to_lock) |
662 | 3 | { |
663 | 3 | int fd = -1; |
664 | 3 | char pidstr[16]; |
665 | 3 | const char *nodename; |
666 | 3 | const char *dirpart; |
667 | 3 | int dirpartlen; |
668 | 3 | struct utsname utsbuf; |
669 | 3 | size_t tnamelen; |
670 | | |
671 | 3 | snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); |
672 | | |
673 | | /* Create a temporary file. */ |
674 | 3 | if ( uname ( &utsbuf ) ) |
675 | 0 | nodename = "unknown"; |
676 | 3 | else |
677 | 3 | nodename = utsbuf.nodename; |
678 | | |
679 | 3 | if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) ) |
680 | 0 | { |
681 | 0 | dirpart = EXTSEP_S; |
682 | 0 | dirpartlen = 1; |
683 | 0 | } |
684 | 3 | else |
685 | 3 | { |
686 | 3 | dirpartlen = dirpart - file_to_lock; |
687 | 3 | dirpart = file_to_lock; |
688 | 3 | } |
689 | | |
690 | 3 | LOCK_all_lockfiles (); |
691 | 3 | h->next = all_lockfiles; |
692 | 3 | all_lockfiles = h; |
693 | | |
694 | 3 | tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; |
695 | 3 | h->tname = xtrymalloc (tnamelen + 1); |
696 | 3 | if (!h->tname) |
697 | 0 | { |
698 | 0 | all_lockfiles = h->next; |
699 | 0 | UNLOCK_all_lockfiles (); |
700 | 0 | xfree (h); |
701 | 0 | return NULL; |
702 | 0 | } |
703 | 3 | h->nodename_len = strlen (nodename); |
704 | | |
705 | 3 | snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); |
706 | 3 | h->nodename_off = strlen (h->tname); |
707 | 3 | snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, |
708 | 3 | "%s.%d", nodename, (int)getpid ()); |
709 | | |
710 | 3 | do |
711 | 3 | { |
712 | 3 | my_set_errno (0); |
713 | 3 | fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, |
714 | 3 | S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); |
715 | 3 | } |
716 | 3 | while (fd == -1 && errno == EINTR); |
717 | | |
718 | 3 | if ( fd == -1 ) |
719 | 0 | { |
720 | 0 | int saveerrno = errno; |
721 | 0 | all_lockfiles = h->next; |
722 | 0 | UNLOCK_all_lockfiles (); |
723 | 0 | my_error_2 (_("failed to create temporary file '%s': %s\n"), |
724 | 0 | h->tname, strerror (errno)); |
725 | 0 | xfree (h->tname); |
726 | 0 | xfree (h); |
727 | 0 | my_set_errno (saveerrno); |
728 | 0 | return NULL; |
729 | 0 | } |
730 | 3 | if ( write (fd, pidstr, 11 ) != 11 ) |
731 | 0 | goto write_failed; |
732 | 3 | if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) |
733 | 0 | goto write_failed; |
734 | 3 | if ( write (fd, "\n", 1 ) != 1 ) |
735 | 0 | goto write_failed; |
736 | 3 | if ( close (fd) ) |
737 | 0 | { |
738 | 0 | if ( errno == EINTR ) |
739 | 0 | fd = -1; |
740 | 0 | goto write_failed; |
741 | 0 | } |
742 | 3 | fd = -1; |
743 | | |
744 | | /* Check whether we support hard links. */ |
745 | 3 | switch (use_hardlinks_p (h->tname)) |
746 | 3 | { |
747 | 3 | case 0: /* Yes. */ |
748 | 3 | break; |
749 | 0 | case 1: /* No. */ |
750 | 0 | unlink (h->tname); |
751 | 0 | h->use_o_excl = 1; |
752 | 0 | break; |
753 | 0 | default: |
754 | 0 | { |
755 | 0 | int saveerrno = errno; |
756 | 0 | my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n" |
757 | 0 | , h->tname, strerror (saveerrno)); |
758 | 0 | my_set_errno (saveerrno); |
759 | 0 | } |
760 | 0 | goto write_failed; |
761 | 3 | } |
762 | | |
763 | 3 | h->lockname = xtrymalloc (strlen (file_to_lock) + 6 ); |
764 | 3 | if (!h->lockname) |
765 | 0 | { |
766 | 0 | int saveerrno = errno; |
767 | 0 | all_lockfiles = h->next; |
768 | 0 | UNLOCK_all_lockfiles (); |
769 | 0 | unlink (h->tname); |
770 | 0 | xfree (h->tname); |
771 | 0 | xfree (h); |
772 | 0 | my_set_errno (saveerrno); |
773 | 0 | return NULL; |
774 | 0 | } |
775 | 3 | strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); |
776 | 3 | UNLOCK_all_lockfiles (); |
777 | | |
778 | 3 | return h; |
779 | | |
780 | 0 | write_failed: |
781 | 0 | { |
782 | 0 | int saveerrno = errno; |
783 | 0 | all_lockfiles = h->next; |
784 | 0 | UNLOCK_all_lockfiles (); |
785 | 0 | my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno)); |
786 | 0 | if ( fd != -1 ) |
787 | 0 | close (fd); |
788 | 0 | unlink (h->tname); |
789 | 0 | xfree (h->tname); |
790 | 0 | xfree (h); |
791 | 0 | my_set_errno (saveerrno); |
792 | 0 | } |
793 | 0 | return NULL; |
794 | 3 | } |
795 | | #endif /*HAVE_POSIX_SYSTEM*/ |
796 | | |
797 | | |
798 | | #ifdef HAVE_DOSISH_SYSTEM |
799 | | /* Locking core for Windows. This version does not need a temporary |
800 | | file but uses the plain lock file along with record locking. We |
801 | | create this file here so that we later only need to do the file |
802 | | locking. For error reporting it is useful to keep the name of the |
803 | | file in the handle. */ |
804 | | static dotlock_t |
805 | | dotlock_create_w32 (dotlock_t h, const char *file_to_lock) |
806 | | { |
807 | | LOCK_all_lockfiles (); |
808 | | h->next = all_lockfiles; |
809 | | all_lockfiles = h; |
810 | | |
811 | | h->lockname = strconcat (file_to_lock, EXTSEP_S "lock", NULL); |
812 | | if (!h->lockname) |
813 | | { |
814 | | all_lockfiles = h->next; |
815 | | UNLOCK_all_lockfiles (); |
816 | | xfree (h); |
817 | | return NULL; |
818 | | } |
819 | | |
820 | | /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE |
821 | | along with FILE_SHARE_DELETE but that does not work due to a race |
822 | | condition: Despite the OPEN_ALWAYS flag CreateFile may return an |
823 | | error and we can't reliable create/open the lock file unless we |
824 | | would wait here until it works - however there are other valid |
825 | | reasons why a lock file can't be created and thus the process |
826 | | would not stop as expected but spin until Windows crashes. Our |
827 | | solution is to keep the lock file open; that does not harm. */ |
828 | | if (any8bitchar (h->lockname)) |
829 | | { |
830 | | wchar_t *wname = utf8_to_wchar (h->lockname); |
831 | | |
832 | | if (wname) |
833 | | h->lockhd = CreateFileW (wname, |
834 | | GENERIC_READ|GENERIC_WRITE, |
835 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
836 | | NULL, OPEN_ALWAYS, 0, NULL); |
837 | | else |
838 | | h->lockhd = INVALID_HANDLE_VALUE; |
839 | | xfree (wname); |
840 | | } |
841 | | else |
842 | | h->lockhd = CreateFileA (h->lockname, |
843 | | GENERIC_READ|GENERIC_WRITE, |
844 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
845 | | NULL, OPEN_ALWAYS, 0, NULL); |
846 | | if (h->lockhd == INVALID_HANDLE_VALUE) |
847 | | { |
848 | | int saveerrno = map_w32_to_errno (GetLastError ()); |
849 | | all_lockfiles = h->next; |
850 | | UNLOCK_all_lockfiles (); |
851 | | my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1)); |
852 | | xfree (h->lockname); |
853 | | xfree (h); |
854 | | my_set_errno (saveerrno); |
855 | | return NULL; |
856 | | } |
857 | | return h; |
858 | | } |
859 | | #endif /*HAVE_DOSISH_SYSTEM*/ |
860 | | |
861 | | |
862 | | /* Create a lockfile for a file name FILE_TO_LOCK and returns an |
863 | | object of type dotlock_t which may be used later to actually acquire |
864 | | the lock. A cleanup routine gets installed to cleanup left over |
865 | | locks or other files used internally by the lock mechanism. |
866 | | |
867 | | Calling this function with NULL does only install the atexit |
868 | | handler and may thus be used to assure that the cleanup is called |
869 | | after all other atexit handlers. |
870 | | |
871 | | This function creates a lock file in the same directory as |
872 | | FILE_TO_LOCK using that name and a suffix of ".lock". Note that on |
873 | | POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is |
874 | | used. |
875 | | |
876 | | FLAGS must be 0. |
877 | | |
878 | | The function returns an new handle which needs to be released using |
879 | | destroy_dotlock but gets also released at the termination of the |
880 | | process. On error NULL is returned. |
881 | | */ |
882 | | |
883 | | dotlock_t |
884 | | dotlock_create (const char *file_to_lock, unsigned int flags) |
885 | 3 | { |
886 | 3 | static int initialized; |
887 | 3 | dotlock_t h; |
888 | | |
889 | 3 | if ( !initialized ) |
890 | 1 | { |
891 | 1 | atexit (dotlock_remove_lockfiles); |
892 | 1 | initialized = 1; |
893 | 1 | } |
894 | | |
895 | 3 | if ( !file_to_lock ) |
896 | 0 | return NULL; /* Only initialization was requested. */ |
897 | | |
898 | 3 | if (flags) |
899 | 0 | { |
900 | 0 | my_set_errno (EINVAL); |
901 | 0 | return NULL; |
902 | 0 | } |
903 | | |
904 | 3 | h = xtrycalloc (1, sizeof *h); |
905 | 3 | if (!h) |
906 | 0 | return NULL; |
907 | 3 | h->extra_fd = -1; |
908 | | |
909 | 3 | if (never_lock) |
910 | 0 | { |
911 | 0 | h->disable = 1; |
912 | 0 | LOCK_all_lockfiles (); |
913 | 0 | h->next = all_lockfiles; |
914 | 0 | all_lockfiles = h; |
915 | 0 | UNLOCK_all_lockfiles (); |
916 | 0 | return h; |
917 | 0 | } |
918 | | |
919 | | #ifdef HAVE_DOSISH_SYSTEM |
920 | | return dotlock_create_w32 (h, file_to_lock); |
921 | | #else /*!HAVE_DOSISH_SYSTEM */ |
922 | 3 | return dotlock_create_unix (h, file_to_lock); |
923 | 3 | #endif /*!HAVE_DOSISH_SYSTEM*/ |
924 | 3 | } |
925 | | |
926 | | |
927 | | |
928 | | /* Convenience function to store a file descriptor (or any other |
929 | | integer value) in the context of handle H. */ |
930 | | void |
931 | | dotlock_set_fd (dotlock_t h, int fd) |
932 | 0 | { |
933 | 0 | h->extra_fd = fd; |
934 | 0 | } |
935 | | |
936 | | /* Convenience function to retrieve a file descriptor (or any other |
937 | | integer value) stored in the context of handle H. */ |
938 | | int |
939 | | dotlock_get_fd (dotlock_t h) |
940 | 0 | { |
941 | 0 | return h->extra_fd; |
942 | 0 | } |
943 | | |
944 | | |
945 | | |
946 | | #ifdef HAVE_POSIX_SYSTEM |
947 | | /* Unix specific code of destroy_dotlock. */ |
948 | | static void |
949 | | dotlock_destroy_unix (dotlock_t h) |
950 | 3 | { |
951 | 3 | if (h->locked && h->lockname) |
952 | 0 | unlink (h->lockname); |
953 | 3 | if (h->tname && !h->use_o_excl) |
954 | 3 | unlink (h->tname); |
955 | 3 | xfree (h->tname); |
956 | 3 | } |
957 | | #endif /*HAVE_POSIX_SYSTEM*/ |
958 | | |
959 | | |
960 | | #ifdef HAVE_DOSISH_SYSTEM |
961 | | /* Windows specific code of destroy_dotlock. */ |
962 | | static void |
963 | | dotlock_destroy_w32 (dotlock_t h) |
964 | | { |
965 | | if (h->locked) |
966 | | { |
967 | | OVERLAPPED ovl; |
968 | | |
969 | | memset (&ovl, 0, sizeof ovl); |
970 | | UnlockFileEx (h->lockhd, 0, 1, 0, &ovl); |
971 | | } |
972 | | CloseHandle (h->lockhd); |
973 | | } |
974 | | #endif /*HAVE_DOSISH_SYSTEM*/ |
975 | | |
976 | | |
977 | | /* Destroy the lock handle H and release the lock. */ |
978 | | void |
979 | | dotlock_destroy (dotlock_t h) |
980 | 3 | { |
981 | 3 | dotlock_t hprev, htmp; |
982 | | |
983 | 3 | if ( !h ) |
984 | 0 | return; |
985 | | |
986 | | /* First remove the handle from our global list of all locks. */ |
987 | 3 | LOCK_all_lockfiles (); |
988 | 3 | for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) |
989 | 1 | if (htmp == h) |
990 | 1 | { |
991 | 1 | if (hprev) |
992 | 0 | hprev->next = htmp->next; |
993 | 1 | else |
994 | 1 | all_lockfiles = htmp->next; |
995 | 1 | h->next = NULL; |
996 | 1 | break; |
997 | 1 | } |
998 | 3 | UNLOCK_all_lockfiles (); |
999 | | |
1000 | | /* Then destroy the lock. */ |
1001 | 3 | if (!h->disable) |
1002 | 3 | { |
1003 | | #ifdef HAVE_DOSISH_SYSTEM |
1004 | | dotlock_destroy_w32 (h); |
1005 | | #else /* !HAVE_DOSISH_SYSTEM */ |
1006 | 3 | dotlock_destroy_unix (h); |
1007 | 3 | #endif /* HAVE_DOSISH_SYSTEM */ |
1008 | 3 | xfree (h->lockname); |
1009 | 3 | } |
1010 | 3 | xfree(h); |
1011 | 3 | } |
1012 | | |
1013 | | |
1014 | | |
1015 | | #ifdef HAVE_POSIX_SYSTEM |
1016 | | /* Unix specific code of make_dotlock. Returns 0 on success and -1 on |
1017 | | error. */ |
1018 | | static int |
1019 | | dotlock_take_unix (dotlock_t h, long timeout) |
1020 | 3 | { |
1021 | 3 | int wtime = 0; |
1022 | 3 | int sumtime = 0; |
1023 | 3 | int pid; |
1024 | 3 | int lastpid = -1; |
1025 | 3 | int ownerchanged; |
1026 | 3 | const char *maybe_dead=""; |
1027 | 3 | int same_node; |
1028 | 3 | int saveerrno; |
1029 | 3 | int fd; |
1030 | | |
1031 | 3 | again: |
1032 | 3 | if (h->use_o_excl) |
1033 | 0 | { |
1034 | | /* No hardlink support - use open(O_EXCL). */ |
1035 | 0 | do |
1036 | 0 | { |
1037 | 0 | my_set_errno (0); |
1038 | 0 | fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL, |
1039 | 0 | S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); |
1040 | 0 | } |
1041 | 0 | while (fd == -1 && errno == EINTR); |
1042 | |
|
1043 | 0 | if (fd == -1 && errno == EEXIST) |
1044 | 0 | ; /* Lock held by another process. */ |
1045 | 0 | else if (fd == -1) |
1046 | 0 | { |
1047 | 0 | saveerrno = errno; |
1048 | 0 | my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n", |
1049 | 0 | h->lockname, strerror (saveerrno)); |
1050 | 0 | my_set_errno (saveerrno); |
1051 | 0 | return -1; |
1052 | 0 | } |
1053 | 0 | else |
1054 | 0 | { |
1055 | 0 | char pidstr[16]; |
1056 | |
|
1057 | 0 | snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); |
1058 | 0 | if (write (fd, pidstr, 11 ) == 11 |
1059 | 0 | && write (fd, h->tname + h->nodename_off,h->nodename_len) |
1060 | 0 | == h->nodename_len |
1061 | 0 | && write (fd, "\n", 1) == 1 |
1062 | 0 | && !close (fd)) |
1063 | 0 | { |
1064 | 0 | h->locked = 1; |
1065 | 0 | return 0; |
1066 | 0 | } |
1067 | | /* Write error. */ |
1068 | 0 | saveerrno = errno; |
1069 | 0 | my_error_2 ("lock not made: writing to '%s' failed: %s\n", |
1070 | 0 | h->lockname, strerror (errno)); |
1071 | 0 | close (fd); |
1072 | 0 | unlink (h->lockname); |
1073 | 0 | my_set_errno (saveerrno); |
1074 | 0 | return -1; |
1075 | 0 | } |
1076 | 0 | } |
1077 | 3 | else /* Standard method: Use hardlinks. */ |
1078 | 3 | { |
1079 | 3 | struct stat sb; |
1080 | | |
1081 | | /* We ignore the return value of link() because it is unreliable. */ |
1082 | 3 | (void) link (h->tname, h->lockname); |
1083 | | |
1084 | 3 | if (stat (h->tname, &sb)) |
1085 | 0 | { |
1086 | 0 | saveerrno = errno; |
1087 | 0 | my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", |
1088 | 0 | strerror (errno)); |
1089 | | /* In theory this might be a severe error: It is possible |
1090 | | that link succeeded but stat failed due to changed |
1091 | | permissions. We can't do anything about it, though. */ |
1092 | 0 | my_set_errno (saveerrno); |
1093 | 0 | return -1; |
1094 | 0 | } |
1095 | | |
1096 | 3 | if (sb.st_nlink == 2) |
1097 | 3 | { |
1098 | 3 | h->locked = 1; |
1099 | 3 | return 0; /* Okay. */ |
1100 | 3 | } |
1101 | 3 | } |
1102 | | |
1103 | | /* Check for stale lock files. */ |
1104 | 0 | if ( (pid = read_lockfile (h, &same_node, &fd)) == -1 ) |
1105 | 0 | { |
1106 | 0 | if ( errno != ENOENT ) |
1107 | 0 | { |
1108 | 0 | saveerrno = errno; |
1109 | 0 | my_info_0 ("cannot read lockfile\n"); |
1110 | 0 | my_set_errno (saveerrno); |
1111 | 0 | return -1; |
1112 | 0 | } |
1113 | 0 | my_info_0 ("lockfile disappeared\n"); |
1114 | 0 | goto again; |
1115 | 0 | } |
1116 | 0 | else if ( (pid == getpid() && same_node) |
1117 | 0 | || (same_node && kill (pid, 0) && errno == ESRCH) ) |
1118 | | /* Stale lockfile is detected. */ |
1119 | 0 | { |
1120 | 0 | struct stat sb; |
1121 | | |
1122 | | /* Check if it's unlocked during examining the lockfile. */ |
1123 | 0 | if (fstat (fd, &sb) || sb.st_nlink == 0) |
1124 | 0 | { |
1125 | | /* It's gone already by another process. */ |
1126 | 0 | close (fd); |
1127 | 0 | goto again; |
1128 | 0 | } |
1129 | | |
1130 | | /* |
1131 | | * Here, although it's quite _rare_, we have a race condition. |
1132 | | * |
1133 | | * When multiple processes race on a stale lockfile, detecting |
1134 | | * AND removing should be done atomically. That is, to work |
1135 | | * correctly, the file to be removed should be the one which is |
1136 | | * examined for detection. |
1137 | | * |
1138 | | * But, when it's not atomic, consider the case for us where it |
1139 | | * takes some time between the detection and the removal of the |
1140 | | * lockfile. |
1141 | | * |
1142 | | * In this situation, it is possible that the file which was |
1143 | | * detected as stale is already removed by another process and |
1144 | | * then new lockfile is created (by that process or other one). |
1145 | | * |
1146 | | * And it is newly created valid lockfile which is going to be |
1147 | | * removed by us. |
1148 | | * |
1149 | | * Consider this long comment as it expresses possible (long) |
1150 | | * time between fstat above and unlink below; Meanwhile, the |
1151 | | * lockfile in question may be removed and there may be new |
1152 | | * valid one. |
1153 | | * |
1154 | | * In short, when you see the message of removing stale lockfile |
1155 | | * when there are multiple processes for the work, there is |
1156 | | * (very) little possibility something went wrong. |
1157 | | */ |
1158 | | |
1159 | 0 | unlink (h->lockname); |
1160 | 0 | my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); |
1161 | 0 | close (fd); |
1162 | 0 | goto again; |
1163 | 0 | } |
1164 | | |
1165 | 0 | close (fd); |
1166 | 0 | if (lastpid == -1) |
1167 | 0 | lastpid = pid; |
1168 | 0 | ownerchanged = (pid != lastpid); |
1169 | |
|
1170 | 0 | if (timeout) |
1171 | 0 | { |
1172 | 0 | struct timeval tv; |
1173 | | |
1174 | | /* Wait until lock has been released. We use increasing retry |
1175 | | intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s |
1176 | | but reset it if the lock owner meanwhile changed. */ |
1177 | 0 | if (!wtime || ownerchanged) |
1178 | 0 | wtime = 50; |
1179 | 0 | else if (wtime < 800) |
1180 | 0 | wtime *= 2; |
1181 | 0 | else if (wtime == 800) |
1182 | 0 | wtime = 2000; |
1183 | 0 | else if (wtime < 8000) |
1184 | 0 | wtime *= 2; |
1185 | |
|
1186 | 0 | if (timeout > 0) |
1187 | 0 | { |
1188 | 0 | if (wtime > timeout) |
1189 | 0 | wtime = timeout; |
1190 | 0 | timeout -= wtime; |
1191 | 0 | } |
1192 | |
|
1193 | 0 | sumtime += wtime; |
1194 | 0 | if (sumtime >= 1500) |
1195 | 0 | { |
1196 | 0 | sumtime = 0; |
1197 | 0 | my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), |
1198 | 0 | pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); |
1199 | 0 | } |
1200 | | |
1201 | |
|
1202 | 0 | tv.tv_sec = wtime / 1000; |
1203 | 0 | tv.tv_usec = (wtime % 1000) * 1000; |
1204 | 0 | select (0, NULL, NULL, NULL, &tv); |
1205 | 0 | goto again; |
1206 | 0 | } |
1207 | | |
1208 | 0 | my_set_errno (EACCES); |
1209 | 0 | return -1; |
1210 | 0 | } |
1211 | | #endif /*HAVE_POSIX_SYSTEM*/ |
1212 | | |
1213 | | |
1214 | | #ifdef HAVE_DOSISH_SYSTEM |
1215 | | /* Windows specific code of make_dotlock. Returns 0 on success and -1 on |
1216 | | error. */ |
1217 | | static int |
1218 | | dotlock_take_w32 (dotlock_t h, long timeout) |
1219 | | { |
1220 | | int wtime = 0; |
1221 | | int w32err; |
1222 | | OVERLAPPED ovl; |
1223 | | |
1224 | | again: |
1225 | | /* Lock one byte at offset 0. The offset is given by OVL. */ |
1226 | | memset (&ovl, 0, sizeof ovl); |
1227 | | if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK |
1228 | | | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl)) |
1229 | | { |
1230 | | h->locked = 1; |
1231 | | return 0; /* okay */ |
1232 | | } |
1233 | | |
1234 | | w32err = GetLastError (); |
1235 | | if (w32err != ERROR_LOCK_VIOLATION) |
1236 | | { |
1237 | | my_error_2 (_("lock '%s' not made: %s\n"), |
1238 | | h->lockname, w32_strerror (w32err)); |
1239 | | my_set_errno (map_w32_to_errno (w32err)); |
1240 | | return -1; |
1241 | | } |
1242 | | |
1243 | | if (timeout) |
1244 | | { |
1245 | | /* Wait until lock has been released. We use retry intervals of |
1246 | | 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */ |
1247 | | if (!wtime) |
1248 | | wtime = 50; |
1249 | | else if (wtime < 800) |
1250 | | wtime *= 2; |
1251 | | else if (wtime == 800) |
1252 | | wtime = 2000; |
1253 | | else if (wtime < 8000) |
1254 | | wtime *= 2; |
1255 | | |
1256 | | if (timeout > 0) |
1257 | | { |
1258 | | if (wtime > timeout) |
1259 | | wtime = timeout; |
1260 | | timeout -= wtime; |
1261 | | } |
1262 | | |
1263 | | if (wtime >= 800) |
1264 | | my_info_1 (_("waiting for lock %s...\n"), h->lockname); |
1265 | | |
1266 | | Sleep (wtime); |
1267 | | goto again; |
1268 | | } |
1269 | | |
1270 | | my_set_errno (EACCES); |
1271 | | return -1; |
1272 | | } |
1273 | | #endif /*HAVE_DOSISH_SYSTEM*/ |
1274 | | |
1275 | | |
1276 | | /* Take a lock on H. A value of 0 for TIMEOUT returns immediately if |
1277 | | the lock can't be taken, -1 waits forever (hopefully not), other |
1278 | | values wait for TIMEOUT milliseconds. Returns: 0 on success */ |
1279 | | int |
1280 | | dotlock_take (dotlock_t h, long timeout) |
1281 | 3 | { |
1282 | 3 | int ret; |
1283 | | |
1284 | 3 | if ( h->disable ) |
1285 | 0 | return 0; /* Locks are completely disabled. Return success. */ |
1286 | | |
1287 | 3 | if ( h->locked ) |
1288 | 0 | { |
1289 | 0 | my_debug_1 ("Oops, '%s' is already locked\n", h->lockname); |
1290 | 0 | return 0; |
1291 | 0 | } |
1292 | | |
1293 | | #ifdef HAVE_DOSISH_SYSTEM |
1294 | | ret = dotlock_take_w32 (h, timeout); |
1295 | | #else /*!HAVE_DOSISH_SYSTEM*/ |
1296 | 3 | ret = dotlock_take_unix (h, timeout); |
1297 | 3 | #endif /*!HAVE_DOSISH_SYSTEM*/ |
1298 | | |
1299 | 3 | return ret; |
1300 | 3 | } |
1301 | | |
1302 | | |
1303 | | |
1304 | | #ifdef HAVE_POSIX_SYSTEM |
1305 | | /* Unix specific code of release_dotlock. */ |
1306 | | static int |
1307 | | dotlock_release_unix (dotlock_t h) |
1308 | 3 | { |
1309 | 3 | int pid, same_node; |
1310 | 3 | int saveerrno; |
1311 | | |
1312 | 3 | pid = read_lockfile (h, &same_node, NULL); |
1313 | 3 | if ( pid == -1 ) |
1314 | 0 | { |
1315 | 0 | saveerrno = errno; |
1316 | 0 | my_error_0 ("release_dotlock: lockfile error\n"); |
1317 | 0 | my_set_errno (saveerrno); |
1318 | 0 | return -1; |
1319 | 0 | } |
1320 | 3 | if ( pid != getpid() || !same_node ) |
1321 | 0 | { |
1322 | 0 | my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); |
1323 | 0 | my_set_errno (EACCES); |
1324 | 0 | return -1; |
1325 | 0 | } |
1326 | | |
1327 | 3 | if ( unlink( h->lockname ) ) |
1328 | 0 | { |
1329 | 0 | saveerrno = errno; |
1330 | 0 | my_error_1 ("release_dotlock: error removing lockfile '%s'\n", |
1331 | 0 | h->lockname); |
1332 | 0 | my_set_errno (saveerrno); |
1333 | 0 | return -1; |
1334 | 0 | } |
1335 | | /* Fixme: As an extra check we could check whether the link count is |
1336 | | now really at 1. */ |
1337 | 3 | return 0; |
1338 | 3 | } |
1339 | | #endif /*HAVE_POSIX_SYSTEM */ |
1340 | | |
1341 | | |
1342 | | #ifdef HAVE_DOSISH_SYSTEM |
1343 | | /* Windows specific code of release_dotlock. */ |
1344 | | static int |
1345 | | dotlock_release_w32 (dotlock_t h) |
1346 | | { |
1347 | | OVERLAPPED ovl; |
1348 | | |
1349 | | memset (&ovl, 0, sizeof ovl); |
1350 | | if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) |
1351 | | { |
1352 | | int saveerrno = map_w32_to_errno (GetLastError ()); |
1353 | | my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n", |
1354 | | h->lockname, w32_strerror (-1)); |
1355 | | my_set_errno (saveerrno); |
1356 | | return -1; |
1357 | | } |
1358 | | |
1359 | | return 0; |
1360 | | } |
1361 | | #endif /*HAVE_DOSISH_SYSTEM */ |
1362 | | |
1363 | | |
1364 | | /* Release a lock. Returns 0 on success. */ |
1365 | | int |
1366 | | dotlock_release (dotlock_t h) |
1367 | 3 | { |
1368 | 3 | int ret; |
1369 | | |
1370 | | /* To avoid atexit race conditions we first check whether there are |
1371 | | any locks left. It might happen that another atexit handler |
1372 | | tries to release the lock while the atexit handler of this module |
1373 | | already ran and thus H is undefined. */ |
1374 | 3 | LOCK_all_lockfiles (); |
1375 | 3 | ret = !all_lockfiles; |
1376 | 3 | UNLOCK_all_lockfiles (); |
1377 | 3 | if (ret) |
1378 | 0 | return 0; |
1379 | | |
1380 | 3 | if ( h->disable ) |
1381 | 0 | return 0; |
1382 | | |
1383 | 3 | if ( !h->locked ) |
1384 | 0 | { |
1385 | 0 | my_debug_1 ("Oops, '%s' is not locked\n", h->lockname); |
1386 | 0 | return 0; |
1387 | 0 | } |
1388 | | |
1389 | | #ifdef HAVE_DOSISH_SYSTEM |
1390 | | ret = dotlock_release_w32 (h); |
1391 | | #else |
1392 | 3 | ret = dotlock_release_unix (h); |
1393 | 3 | #endif |
1394 | | |
1395 | 3 | if (!ret) |
1396 | 3 | h->locked = 0; |
1397 | 3 | return ret; |
1398 | 3 | } |
1399 | | |
1400 | | |
1401 | | |
1402 | | /* Remove all lockfiles. This is called by the atexit handler |
1403 | | installed by this module but may also be called by other |
1404 | | termination handlers. */ |
1405 | | void |
1406 | | dotlock_remove_lockfiles (void) |
1407 | 1 | { |
1408 | 1 | dotlock_t h, h2; |
1409 | | |
1410 | | /* First set the lockfiles list to NULL so that for example |
1411 | | dotlock_release is aware that this function is currently |
1412 | | running. */ |
1413 | 1 | LOCK_all_lockfiles (); |
1414 | 1 | h = all_lockfiles; |
1415 | 1 | all_lockfiles = NULL; |
1416 | 1 | UNLOCK_all_lockfiles (); |
1417 | | |
1418 | 3 | while ( h ) |
1419 | 2 | { |
1420 | 2 | h2 = h->next; |
1421 | 2 | dotlock_destroy (h); |
1422 | 2 | h = h2; |
1423 | 2 | } |
1424 | 1 | } |