Line data Source code
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 21 : tor_lockfile_lock(const char *filename, int blocking, int *locked_out)
64 : {
65 21 : tor_lockfile_t *result;
66 21 : int fd;
67 21 : *locked_out = 0;
68 :
69 21 : log_info(LD_FS, "Locking \"%s\"", filename);
70 21 : fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
71 21 : if (fd < 0) {
72 0 : log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename,
73 : strerror(errno));
74 0 : return NULL;
75 : }
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 42 : 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 : else
92 0 : *locked_out = 1;
93 0 : close(fd);
94 0 : return NULL;
95 : }
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 21 : result = tor_malloc(sizeof(tor_lockfile_t));
114 21 : result->filename = tor_strdup(filename);
115 21 : result->fd = fd;
116 21 : return result;
117 : }
118 :
119 : /** Release the lock held as <b>lockfile</b>. */
120 : void
121 21 : tor_lockfile_unlock(tor_lockfile_t *lockfile)
122 : {
123 21 : tor_assert(lockfile);
124 :
125 21 : 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 21 : if (flock(lockfile->fd, LOCK_UN) < 0) {
134 0 : log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename,
135 : strerror(errno));
136 : }
137 : #else
138 : /* Closing the lockfile is sufficient. */
139 : #endif /* defined(_WIN32) || ... */
140 :
141 21 : close(lockfile->fd);
142 21 : lockfile->fd = -1;
143 21 : tor_free(lockfile->filename);
144 21 : tor_free(lockfile);
145 21 : }
|