1 /* Copyright 2016, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Alternatively, this software may be distributed under the terms of the
31 * GNU General Public License ("GPL") version 2 as published by the Free
32 * Software Foundation.
33 *
34 * file_lock.c: Implementation for a binary semaphore using a file lock.
35 *
36 * Warning: This relies on flock() which is known to be broken on NFS.
37 *
38 * The file will remain persistent once the lock has been used. Unfortunately,
39 * unlinking the file can introduce a race condition so we leave the file
40 * in place.
41 *
42 * The current process's PID will be written to the file for debug purposes.
43 */
44
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <inttypes.h>
48 #include <limits.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 #include <sys/file.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57
58 #include "flash.h"
59 #include "ipc_lock.h"
60
61 #define SLEEP_INTERVAL_MS 50
62
msecs_to_timespec(int msecs,struct timespec * tmspec)63 static void msecs_to_timespec(int msecs, struct timespec *tmspec)
64 {
65 tmspec->tv_sec = msecs / 1000;
66 tmspec->tv_nsec = (msecs % 1000) * 1000 * 1000;
67 }
68
lock_is_held(struct ipc_lock * lock)69 static int lock_is_held(struct ipc_lock *lock)
70 {
71 return lock->is_held;
72 }
73
test_dir(const char * path)74 static int test_dir(const char *path)
75 {
76 struct stat s;
77
78 if (lstat(path, &s) < 0) {
79 msg_gerr("Cannot stat %s.\n", path);
80 return -1;
81 }
82
83 if (!S_ISDIR(s.st_mode)) {
84 msg_gerr("%s is not a directory.\n", path);
85 return -1;
86 }
87
88 return 0;
89 }
90
file_lock_try_open_or_create(const char * dir,struct ipc_lock * lock)91 static int file_lock_try_open_or_create(const char *dir, struct ipc_lock *lock)
92 {
93 char path[PATH_MAX];
94 if (test_dir(dir))
95 return -1;
96
97 if (snprintf(path, sizeof(path), "%s/%s", dir, lock->filename) < 0)
98 return -1;
99
100 /* 0666: User/Group/Others: read, write. */
101 mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
102 lock->fd = open(path, O_RDWR | O_CREAT, permissions);
103 if (lock->fd < 0) {
104 msg_gerr("Cannot open lockfile %s\n", path);
105 return -1;
106 }
107 /* Ignore potential fchmod() failures since we might not be the owner. */
108 if (fchmod(lock->fd, permissions) == -1)
109 msg_gdbg("Cannot change the permissions of lockfile %s\n", path);
110
111 msg_gdbg("Opened file lock \"%s\"\n", path);
112 return 0;
113 }
114
file_lock_open_or_create(struct ipc_lock * lock)115 static int file_lock_open_or_create(struct ipc_lock *lock)
116 {
117 const char *dirs[] = {
118 #ifndef __ANDROID__
119 // Default modern Linux lock path.
120 "/run/lock",
121 // Fallback to temporary directory.
122 "/tmp",
123 #else
124 // flashrom called as a subprocess with its own SELinux context.
125 "/data/vendor/flashrom/tmp",
126 // Same as above but for case when there is no tmpfs.
127 "/data/vendor/flashrom",
128 // flashrom called from the console/shell. Comes last as a fallback.
129 "/data/local/tmp",
130 #endif
131 };
132
133 if (file_lock_try_open_or_create(dirs[0], lock) == 0)
134 return 0;
135
136 for (size_t i = 1; i < ARRAY_SIZE(dirs); ++i) {
137 msg_gwarn("Trying fallback directory: %s\n", dirs[i]);
138 if (file_lock_try_open_or_create(dirs[i], lock) == 0)
139 return 0;
140 }
141
142 msg_gerr("Failed to find usable directory for file lock\n");
143 return -1;
144 }
145
file_lock_get(struct ipc_lock * lock,int timeout_msecs)146 static int file_lock_get(struct ipc_lock *lock, int timeout_msecs)
147 {
148 int remaining_msecs;
149 struct timespec sleep_interval;
150 int ret;
151
152 if (timeout_msecs == 0)
153 return flock(lock->fd, LOCK_EX | LOCK_NB);
154
155 if (timeout_msecs < 0)
156 remaining_msecs = SLEEP_INTERVAL_MS;
157 else
158 remaining_msecs = timeout_msecs;
159
160 while ((ret = flock(lock->fd, LOCK_EX | LOCK_NB)) != 0) {
161 struct timespec rem;
162
163 if (errno != EWOULDBLOCK) {
164 msg_gerr("Error obtaining lock\n");
165 return -1;
166 }
167
168 msecs_to_timespec(MIN(remaining_msecs, SLEEP_INTERVAL_MS),
169 &sleep_interval);
170
171 while (nanosleep(&sleep_interval, &rem) != 0) {
172 if (errno == EINTR) {
173 sleep_interval = rem;
174 continue;
175 }
176 msg_gerr("nanosleep() failed\n");
177 return -1;
178 }
179
180 if (timeout_msecs < 0)
181 continue;
182
183 remaining_msecs -= SLEEP_INTERVAL_MS;
184 if (remaining_msecs < 0)
185 break;
186 }
187
188 if (ret != 0) {
189 msg_gerr("Timed out waiting for file lock.\n");
190 return -1;
191 }
192
193 return 0;
194 }
195
file_lock_write_pid(struct ipc_lock * lock)196 static int file_lock_write_pid(struct ipc_lock *lock)
197 {
198 ssize_t len;
199 /* PIDs are usually 5 digits, but we'll reserve enough room for
200 a value of 2^32 (10 digits) out of paranoia. */
201 char pid_str[11];
202
203 if (ftruncate(lock->fd, 0) < 0) {
204 msg_gerr("Cannot truncate lockfile\n");
205 return -1;
206 }
207
208 snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)getpid());
209 len = write(lock->fd, pid_str, strlen(pid_str));
210 if (len < 0) {
211 msg_gerr("Cannot write PID to lockfile\n");
212 return -1;
213 }
214
215 return 0;
216 }
217
file_lock_release(struct ipc_lock * lock)218 static void file_lock_release(struct ipc_lock *lock)
219 {
220 if (flock(lock->fd, LOCK_UN) < 0)
221 msg_gerr("Cannot release lock\n");
222
223 if (close(lock->fd) < 0)
224 msg_gerr("Cannot close lockfile\n");
225 }
226
227 /*
228 * timeout <0 = no timeout (try forever)
229 * timeout 0 = do not wait (return immediately)
230 * timeout >0 = wait up to $timeout milliseconds
231 *
232 * returns 0 to indicate lock acquired
233 * returns >0 to indicate lock was already held
234 * returns <0 to indicate failed to acquire lock
235 */
acquire_lock(struct ipc_lock * lock,int timeout_msecs)236 int acquire_lock(struct ipc_lock *lock, int timeout_msecs)
237 {
238 /* check if it is already held */
239 if (lock_is_held(lock))
240 return 1;
241
242 if (file_lock_open_or_create(lock))
243 return -1;
244
245 if (file_lock_get(lock, timeout_msecs)) {
246 lock->is_held = 0;
247 close(lock->fd);
248 return -1;
249 } else {
250 lock->is_held = 1;
251 }
252
253 /*
254 * Write PID to lockfile for debug purposes. Failure to write to
255 * the file should not be considered fatal. There might be something
256 * bad happening with the filesystem, but the lock has already been
257 * obtained and we may need our tools for diagnostics and repairs
258 * so we should continue anyway.
259 */
260 file_lock_write_pid(lock);
261 return 0;
262 }
263
264 /*
265 * returns 0 if lock was released successfully
266 * returns -1 if lock had not been held before the call
267 */
release_lock(struct ipc_lock * lock)268 int release_lock(struct ipc_lock *lock)
269 {
270 if (lock_is_held(lock)) {
271 file_lock_release(lock);
272 lock->is_held = 0;
273 return 0;
274 }
275
276 msg_ginfo("%s called but lock was not held on %s.\n",
277 __func__, lock->filename);
278 return -1;
279 }
280