/src/tor/src/lib/fs/lockfile.c
Line | Count | Source |
1 | | /* Copyright (c) 2003-2004, Roger Dingledine |
2 | | * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. |
3 | | * Copyright (c) 2007-2021, The Tor Project, Inc. */ |
4 | | /* See LICENSE for licensing information */ |
5 | | |
6 | | /** |
7 | | * \file lockfile.c |
8 | | * |
9 | | * \brief Implements lock files to prevent two Tor processes from using the |
10 | | * same data directory at the same time. |
11 | | **/ |
12 | | |
13 | | #include "orconfig.h" |
14 | | #include "lib/fs/files.h" |
15 | | #include "lib/fs/lockfile.h" |
16 | | #include "lib/log/log.h" |
17 | | #include "lib/log/util_bug.h" |
18 | | #include "lib/malloc/malloc.h" |
19 | | |
20 | | #ifdef HAVE_SYS_FILE_H |
21 | | #include <sys/file.h> |
22 | | #endif |
23 | | #ifdef HAVE_FCNTL_H |
24 | | #include <fcntl.h> |
25 | | #endif |
26 | | #ifdef HAVE_UNISTD_H |
27 | | #include <unistd.h> |
28 | | #endif |
29 | | #ifdef _WIN32 |
30 | | #include <windows.h> |
31 | | #include <sys/locking.h> |
32 | | #endif |
33 | | |
34 | | #include <errno.h> |
35 | | #include <string.h> |
36 | | |
37 | | /** Represents a lockfile on which we hold the lock. */ |
38 | | struct tor_lockfile_t { |
39 | | /** Name of the file */ |
40 | | char *filename; |
41 | | /** File descriptor used to hold the file open */ |
42 | | int fd; |
43 | | }; |
44 | | |
45 | | /** Try to get a lock on the lockfile <b>filename</b>, creating it as |
46 | | * necessary. If someone else has the lock and <b>blocking</b> is true, |
47 | | * wait until the lock is available. Otherwise return immediately whether |
48 | | * we succeeded or not. |
49 | | * |
50 | | * Set *<b>locked_out</b> to true if somebody else had the lock, and to false |
51 | | * otherwise. |
52 | | * |
53 | | * Return a <b>tor_lockfile_t</b> on success, NULL on failure. |
54 | | * |
55 | | * (Implementation note: because we need to fall back to fcntl on some |
56 | | * platforms, these locks are per-process, not per-thread. If you want |
57 | | * to do in-process locking, use tor_mutex_t like a normal person. |
58 | | * On Windows, when <b>blocking</b> is true, the maximum time that |
59 | | * is actually waited is 10 seconds, after which NULL is returned |
60 | | * and <b>locked_out</b> is set to 1.) |
61 | | */ |
62 | | tor_lockfile_t * |
63 | | tor_lockfile_lock(const char *filename, int blocking, int *locked_out) |
64 | 0 | { |
65 | 0 | tor_lockfile_t *result; |
66 | 0 | int fd; |
67 | 0 | *locked_out = 0; |
68 | |
|
69 | 0 | log_info(LD_FS, "Locking \"%s\"", filename); |
70 | 0 | fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); |
71 | 0 | if (fd < 0) { |
72 | 0 | log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, |
73 | 0 | strerror(errno)); |
74 | 0 | return NULL; |
75 | 0 | } |
76 | | |
77 | | #ifdef _WIN32 |
78 | | _lseek(fd, 0, SEEK_SET); |
79 | | if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) { |
80 | | if (errno != EACCES && errno != EDEADLOCK) |
81 | | log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); |
82 | | else |
83 | | *locked_out = 1; |
84 | | close(fd); |
85 | | return NULL; |
86 | | } |
87 | | #elif defined(HAVE_FLOCK) |
88 | 0 | if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) { |
89 | 0 | if (errno != EWOULDBLOCK) |
90 | 0 | log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); |
91 | 0 | else |
92 | 0 | *locked_out = 1; |
93 | 0 | close(fd); |
94 | 0 | return NULL; |
95 | 0 | } |
96 | | #else |
97 | | { |
98 | | struct flock lock; |
99 | | memset(&lock, 0, sizeof(lock)); |
100 | | lock.l_type = F_WRLCK; |
101 | | lock.l_whence = SEEK_SET; |
102 | | if (fcntl(fd, blocking ? F_SETLKW : F_SETLK, &lock) < 0) { |
103 | | if (errno != EACCES && errno != EAGAIN) |
104 | | log_warn(LD_FS, "Couldn't lock \"%s\": %s", filename, strerror(errno)); |
105 | | else |
106 | | *locked_out = 1; |
107 | | close(fd); |
108 | | return NULL; |
109 | | } |
110 | | } |
111 | | #endif /* defined(_WIN32) || ... */ |
112 | | |
113 | 0 | result = tor_malloc(sizeof(tor_lockfile_t)); |
114 | 0 | result->filename = tor_strdup(filename); |
115 | 0 | result->fd = fd; |
116 | 0 | return result; |
117 | 0 | } |
118 | | |
119 | | /** Release the lock held as <b>lockfile</b>. */ |
120 | | void |
121 | | tor_lockfile_unlock(tor_lockfile_t *lockfile) |
122 | 0 | { |
123 | 0 | tor_assert(lockfile); |
124 | |
|
125 | 0 | log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); |
126 | | #ifdef _WIN32 |
127 | | _lseek(lockfile->fd, 0, SEEK_SET); |
128 | | if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) { |
129 | | log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, |
130 | | strerror(errno)); |
131 | | } |
132 | | #elif defined(HAVE_FLOCK) |
133 | 0 | if (flock(lockfile->fd, LOCK_UN) < 0) { |
134 | 0 | log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename, |
135 | 0 | strerror(errno)); |
136 | 0 | } |
137 | | #else |
138 | | /* Closing the lockfile is sufficient. */ |
139 | | #endif /* defined(_WIN32) || ... */ |
140 | |
|
141 | 0 | close(lockfile->fd); |
142 | 0 | lockfile->fd = -1; |
143 | 0 | tor_free(lockfile->filename); |
144 | | tor_free(lockfile); |
145 | 0 | } |