/src/openvswitch/lib/lockfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
2 | | * |
3 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | * you may not use this file except in compliance with the License. |
5 | | * You may obtain a copy of the License at: |
6 | | * |
7 | | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | | * |
9 | | * Unless required by applicable law or agreed to in writing, software |
10 | | * distributed under the License is distributed on an "AS IS" BASIS, |
11 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | * See the License for the specific language governing permissions and |
13 | | * limitations under the License. |
14 | | */ |
15 | | |
16 | | #include <config.h> |
17 | | |
18 | | #include "lockfile.h" |
19 | | |
20 | | #include <errno.h> |
21 | | #include <fcntl.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <sys/stat.h> |
25 | | #include <unistd.h> |
26 | | |
27 | | #include "coverage.h" |
28 | | #include "hash.h" |
29 | | #include "openvswitch/hmap.h" |
30 | | #include "ovs-thread.h" |
31 | | #include "timeval.h" |
32 | | #include "util.h" |
33 | | #include "openvswitch/vlog.h" |
34 | | |
35 | | VLOG_DEFINE_THIS_MODULE(lockfile); |
36 | | |
37 | | COVERAGE_DEFINE(lockfile_lock); |
38 | | COVERAGE_DEFINE(lockfile_error); |
39 | | COVERAGE_DEFINE(lockfile_unlock); |
40 | | |
41 | | struct lockfile { |
42 | | struct hmap_node hmap_node; |
43 | | char *name; |
44 | | dev_t device; |
45 | | ino_t inode; |
46 | | int fd; |
47 | | HANDLE lock_handle; |
48 | | }; |
49 | | |
50 | | /* Lock table. |
51 | | * |
52 | | * We have to do this stupid dance because POSIX says that closing *any* file |
53 | | * descriptor for a file on which a process holds a lock drops *all* locks on |
54 | | * that file. That means that we can't afford to open a lockfile more than |
55 | | * once. */ |
56 | | static struct ovs_mutex lock_table_mutex = OVS_MUTEX_INITIALIZER; |
57 | | static struct hmap lock_table__ = HMAP_INITIALIZER(&lock_table__); |
58 | | static struct hmap *const lock_table OVS_GUARDED_BY(lock_table_mutex) |
59 | | = &lock_table__; |
60 | | |
61 | | static void lockfile_unhash(struct lockfile *); |
62 | | static int lockfile_try_lock(const char *name, pid_t *pidp, |
63 | | struct lockfile **lockfilep) |
64 | | OVS_REQUIRES(lock_table_mutex); |
65 | | static void lockfile_do_unlock(struct lockfile * lockfile) |
66 | | OVS_REQUIRES(lock_table_mutex); |
67 | | |
68 | | /* Returns the name of the lockfile that would be created for locking a file |
69 | | * named 'filename_'. The caller is responsible for freeing the returned name, |
70 | | * with free(), when it is no longer needed. */ |
71 | | char * |
72 | | lockfile_name(const char *filename_) |
73 | 0 | { |
74 | 0 | char *filename; |
75 | 0 | const char *slash; |
76 | 0 | char *lockname; |
77 | | |
78 | | /* If 'filename_' is a symlink, base the name of the lockfile on the |
79 | | * symlink's target rather than the name of the symlink. That way, if a |
80 | | * file is symlinked, but there is no symlink for its lockfile, then there |
81 | | * is only a single lockfile for both the source and the target of the |
82 | | * symlink, not one for each. */ |
83 | 0 | filename = follow_symlinks(filename_); |
84 | 0 | slash = strrchr(filename, '/'); |
85 | |
|
86 | | #ifdef _WIN32 |
87 | | char *backslash = strrchr(filename, '\\'); |
88 | | if (backslash && (!slash || backslash > slash)) { |
89 | | slash = backslash; |
90 | | } |
91 | | #endif |
92 | |
|
93 | 0 | lockname = (slash |
94 | 0 | ? xasprintf("%.*s/.%s.~lock~", |
95 | 0 | (int) (slash - filename), filename, slash + 1) |
96 | 0 | : xasprintf(".%s.~lock~", filename)); |
97 | 0 | free(filename); |
98 | |
|
99 | 0 | return lockname; |
100 | 0 | } |
101 | | |
102 | | /* Locks the configuration file against modification by other processes and |
103 | | * re-reads it from disk. |
104 | | * |
105 | | * Returns 0 on success, otherwise a positive errno value. On success, |
106 | | * '*lockfilep' is set to point to a new "struct lockfile *" that may be |
107 | | * unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to |
108 | | * NULL. Will not block if the lock cannot be immediately acquired. */ |
109 | | int |
110 | | lockfile_lock(const char *file, struct lockfile **lockfilep) |
111 | 0 | { |
112 | | /* Only exclusive ("write") locks are supported. This is not a problem |
113 | | * because the Open vSwitch code that currently uses lock files does so in |
114 | | * stylized ways such that any number of readers may access a file while it |
115 | | * is being written. */ |
116 | 0 | char *lock_name; |
117 | 0 | pid_t pid; |
118 | 0 | int error; |
119 | |
|
120 | 0 | COVERAGE_INC(lockfile_lock); |
121 | |
|
122 | 0 | lock_name = lockfile_name(file); |
123 | |
|
124 | 0 | ovs_mutex_lock(&lock_table_mutex); |
125 | 0 | error = lockfile_try_lock(lock_name, &pid, lockfilep); |
126 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
127 | |
|
128 | 0 | if (error) { |
129 | 0 | COVERAGE_INC(lockfile_error); |
130 | 0 | if (error == EACCES) { |
131 | 0 | error = EAGAIN; |
132 | 0 | } |
133 | 0 | if (pid == getpid()) { |
134 | 0 | VLOG_WARN("%s: cannot lock file because this process has already " |
135 | 0 | "locked it", lock_name); |
136 | 0 | } else if (pid) { |
137 | 0 | VLOG_WARN("%s: cannot lock file because it is already locked by " |
138 | 0 | "pid %ld", lock_name, (long int) pid); |
139 | 0 | } else { |
140 | 0 | VLOG_WARN("%s: failed to lock file: %s", |
141 | 0 | lock_name, ovs_strerror(error)); |
142 | 0 | } |
143 | 0 | } |
144 | |
|
145 | 0 | free(lock_name); |
146 | 0 | return error; |
147 | 0 | } |
148 | | |
149 | | /* Unlocks 'lockfile', which must have been created by a call to |
150 | | * lockfile_lock(), and frees 'lockfile'. */ |
151 | | void |
152 | | lockfile_unlock(struct lockfile *lockfile) |
153 | 0 | { |
154 | 0 | if (lockfile) { |
155 | 0 | ovs_mutex_lock(&lock_table_mutex); |
156 | 0 | lockfile_do_unlock(lockfile); |
157 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
158 | |
|
159 | 0 | COVERAGE_INC(lockfile_unlock); |
160 | 0 | free(lockfile->name); |
161 | 0 | free(lockfile); |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | /* Marks all the currently locked lockfiles as no longer locked. It makes |
166 | | * sense to call this function after fork(), because a child created by fork() |
167 | | * does not hold its parents' locks. */ |
168 | | void |
169 | | lockfile_postfork(void) |
170 | 0 | { |
171 | 0 | struct lockfile *lockfile; |
172 | |
|
173 | 0 | ovs_mutex_lock(&lock_table_mutex); |
174 | 0 | HMAP_FOR_EACH (lockfile, hmap_node, lock_table) { |
175 | 0 | if (lockfile->fd >= 0) { |
176 | 0 | VLOG_WARN("%s: child does not inherit lock", lockfile->name); |
177 | 0 | lockfile_unhash(lockfile); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
181 | 0 | } |
182 | | |
183 | | static uint32_t |
184 | | lockfile_hash(dev_t device, ino_t inode) |
185 | 0 | { |
186 | 0 | return hash_bytes(&device, sizeof device, |
187 | 0 | hash_bytes(&inode, sizeof inode, 0)); |
188 | 0 | } |
189 | | |
190 | | static struct lockfile * |
191 | | lockfile_find(dev_t device, ino_t inode) OVS_REQUIRES(lock_table_mutex) |
192 | 0 | { |
193 | 0 | struct lockfile *lockfile; |
194 | |
|
195 | 0 | HMAP_FOR_EACH_WITH_HASH (lockfile, hmap_node, |
196 | 0 | lockfile_hash(device, inode), lock_table) { |
197 | 0 | if (lockfile->device == device && lockfile->inode == inode) { |
198 | 0 | return lockfile; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | return NULL; |
202 | 0 | } |
203 | | |
204 | | static void |
205 | | lockfile_unhash(struct lockfile *lockfile) OVS_REQUIRES(lock_table_mutex) |
206 | 0 | { |
207 | 0 | if (lockfile->fd >= 0) { |
208 | 0 | close(lockfile->fd); |
209 | 0 | lockfile->fd = -1; |
210 | 0 | hmap_remove(lock_table, &lockfile->hmap_node); |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | static struct lockfile * |
215 | | lockfile_register(const char *name, dev_t device, ino_t inode, int fd) |
216 | | OVS_REQUIRES(lock_table_mutex) |
217 | 0 | { |
218 | 0 | struct lockfile *lockfile; |
219 | |
|
220 | 0 | lockfile = lockfile_find(device, inode); |
221 | 0 | if (lockfile) { |
222 | 0 | VLOG_ERR("%s: lock file disappeared and reappeared!", name); |
223 | 0 | lockfile_unhash(lockfile); |
224 | 0 | } |
225 | |
|
226 | 0 | lockfile = xmalloc(sizeof *lockfile); |
227 | 0 | lockfile->name = xstrdup(name); |
228 | 0 | lockfile->device = device; |
229 | 0 | lockfile->inode = inode; |
230 | 0 | lockfile->fd = fd; |
231 | 0 | hmap_insert(lock_table, &lockfile->hmap_node, |
232 | 0 | lockfile_hash(device, inode)); |
233 | 0 | return lockfile; |
234 | 0 | } |
235 | | |
236 | | #ifdef _WIN32 |
237 | | static void |
238 | | lockfile_do_unlock(struct lockfile *lockfile) |
239 | | OVS_REQUIRES(lock_table_mutex) |
240 | | { |
241 | | if (lockfile->fd >= 0) { |
242 | | OVERLAPPED overl; |
243 | | overl.hEvent = 0; |
244 | | overl.Offset = 0; |
245 | | overl.OffsetHigh = 0; |
246 | | UnlockFileEx(lockfile->lock_handle, 0, 1, 0, &overl); |
247 | | |
248 | | close(lockfile->fd); |
249 | | lockfile->fd = -1; |
250 | | } |
251 | | } |
252 | | |
253 | | static int |
254 | | lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) |
255 | | OVS_REQUIRES(lock_table_mutex) |
256 | | { |
257 | | HANDLE lock_handle; |
258 | | BOOL retval; |
259 | | OVERLAPPED overl; |
260 | | struct lockfile *lockfile; |
261 | | int fd; |
262 | | |
263 | | *pidp = 0; |
264 | | |
265 | | fd = open(name, O_RDWR | O_CREAT, 0600); |
266 | | if (fd < 0) { |
267 | | VLOG_WARN("%s: failed to open lock file: %s", |
268 | | name, ovs_strerror(errno)); |
269 | | return errno; |
270 | | } |
271 | | |
272 | | lock_handle = (HANDLE)_get_osfhandle(fd); |
273 | | if (lock_handle < 0) { |
274 | | VLOG_WARN("%s: failed to get the file handle: %s", |
275 | | name, ovs_strerror(errno)); |
276 | | return errno; |
277 | | } |
278 | | |
279 | | /* Lock the file 'name' for the region that includes just the first |
280 | | * byte. */ |
281 | | overl.hEvent = 0; |
282 | | overl.Offset = 0; |
283 | | overl.OffsetHigh = 0; |
284 | | retval = LockFileEx(lock_handle, LOCKFILE_EXCLUSIVE_LOCK |
285 | | | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &overl); |
286 | | if (!retval) { |
287 | | VLOG_DBG("Failed to lock file : %s", ovs_lasterror_to_string()); |
288 | | *pidp = getpid(); |
289 | | return EDEADLK; |
290 | | } |
291 | | |
292 | | lockfile = xmalloc(sizeof *lockfile); |
293 | | lockfile->name = xstrdup(name); |
294 | | lockfile->fd = fd; |
295 | | lockfile->lock_handle = lock_handle; |
296 | | |
297 | | *lockfilep = lockfile; |
298 | | return 0; |
299 | | } |
300 | | #else /* !_WIN32 */ |
301 | | static void |
302 | | lockfile_do_unlock(struct lockfile *lockfile) |
303 | 0 | { |
304 | 0 | lockfile_unhash(lockfile); |
305 | 0 | } |
306 | | |
307 | | static int |
308 | | lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) |
309 | | OVS_REQUIRES(lock_table_mutex) |
310 | 0 | { |
311 | 0 | struct flock l; |
312 | 0 | struct stat s; |
313 | 0 | int error; |
314 | 0 | int fd; |
315 | |
|
316 | 0 | *lockfilep = NULL; |
317 | 0 | *pidp = 0; |
318 | | |
319 | | /* Check whether we've already got a lock on that file. */ |
320 | 0 | if (!stat(name, &s)) { |
321 | 0 | if (lockfile_find(s.st_dev, s.st_ino)) { |
322 | 0 | *pidp = getpid(); |
323 | 0 | return EDEADLK; |
324 | 0 | } |
325 | 0 | } else if (errno != ENOENT) { |
326 | 0 | VLOG_WARN("%s: failed to stat lock file: %s", |
327 | 0 | name, ovs_strerror(errno)); |
328 | 0 | return errno; |
329 | 0 | } |
330 | | |
331 | | /* Open the lock file. */ |
332 | 0 | fd = open(name, O_RDWR | O_CREAT, 0600); |
333 | 0 | if (fd < 0) { |
334 | 0 | VLOG_WARN("%s: failed to open lock file: %s", |
335 | 0 | name, ovs_strerror(errno)); |
336 | 0 | return errno; |
337 | 0 | } |
338 | | |
339 | | /* Get the inode and device number for the lock table. */ |
340 | 0 | if (fstat(fd, &s)) { |
341 | 0 | VLOG_ERR("%s: failed to fstat lock file: %s", |
342 | 0 | name, ovs_strerror(errno)); |
343 | 0 | close(fd); |
344 | 0 | return errno; |
345 | 0 | } |
346 | | |
347 | | /* Try to lock the file. */ |
348 | 0 | memset(&l, 0, sizeof l); |
349 | 0 | l.l_type = F_WRLCK; |
350 | 0 | l.l_whence = SEEK_SET; |
351 | 0 | l.l_start = 0; |
352 | 0 | l.l_len = 0; |
353 | |
|
354 | 0 | error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0; |
355 | |
|
356 | 0 | if (!error) { |
357 | 0 | *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd); |
358 | 0 | } else { |
359 | 0 | if (!fcntl(fd, F_GETLK, &l) && l.l_type != F_UNLCK) { |
360 | 0 | *pidp = l.l_pid; |
361 | 0 | } |
362 | 0 | close(fd); |
363 | 0 | } |
364 | 0 | return error; |
365 | 0 | } |
366 | | #endif |