xref: /aosp_15_r20/external/flashrom/file_lock.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
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