]>
Commit | Line | Data |
---|---|---|
2c37ca7c | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
27c6d415 | 2 | /* |
2c37ca7c | 3 | * This file is part of libmount from util-linux project. |
27c6d415 | 4 | * |
2c37ca7c KZ |
5 | * Copyright (C) 2009-2018 Karel Zak <kzak@redhat.com> |
6 | * | |
7 | * libmount is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU Lesser General Public License as published by | |
9 | * the Free Software Foundation; either version 2.1 of the License, or | |
10 | * (at your option) any later version. | |
27c6d415 | 11 | */ |
192c6aad KZ |
12 | |
13 | /** | |
14 | * SECTION: lock | |
63de90d4 | 15 | * @title: Locking |
61394559 KZ |
16 | * @short_description: locking methods for utab or another libmount files |
17 | * | |
1a02c7a7 | 18 | * Since v2.39 libmount does not support classic mtab locking. Now all is based |
61394559 | 19 | * on flock only. |
192c6aad | 20 | * |
192c6aad | 21 | */ |
27c6d415 KZ |
22 | #include <sys/time.h> |
23 | #include <time.h> | |
24 | #include <signal.h> | |
25 | #include <fcntl.h> | |
26 | #include <limits.h> | |
5eb00eb4 | 27 | #include <sys/file.h> |
27c6d415 | 28 | |
60f25dea | 29 | #include "strutils.h" |
69f1cad4 | 30 | #include "closestream.h" |
27c6d415 | 31 | #include "pathnames.h" |
27c6d415 | 32 | #include "mountP.h" |
cd2876d2 | 33 | #include "monotonic.h" |
27c6d415 KZ |
34 | |
35 | /* | |
36 | * lock handler | |
37 | */ | |
68164f6c | 38 | struct libmnt_lock { |
87938635 | 39 | int refcount; /* reference counter */ |
27c6d415 | 40 | char *lockfile; /* path to lock file (e.g. /etc/mtab~) */ |
27c6d415 | 41 | int lockfile_fd; /* lock file descriptor */ |
5976114f | 42 | |
63d79371 ZJS |
43 | bool locked, /* do we own the lock? */ |
44 | sigblock; /* block signals when locked */ | |
5976114f KZ |
45 | |
46 | sigset_t oldsigmask; | |
27c6d415 KZ |
47 | }; |
48 | ||
49 | ||
50 | /** | |
51 | * mnt_new_lock: | |
0f32f1e2 | 52 | * @datafile: the file that should be covered by the lock |
61394559 | 53 | * @id: ignored by library |
27c6d415 | 54 | * |
192c6aad | 55 | * Returns: newly allocated lock handler or NULL on case of error. |
27c6d415 | 56 | */ |
61394559 | 57 | struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id __attribute__((__unused__))) |
27c6d415 | 58 | { |
68164f6c | 59 | struct libmnt_lock *ml = NULL; |
61394559 | 60 | char *lo = NULL; |
5eb00eb4 | 61 | size_t losz; |
27c6d415 | 62 | |
37290a53 KZ |
63 | if (!datafile) |
64 | return NULL; | |
4569bbea | 65 | |
5eb00eb4 KZ |
66 | losz = strlen(datafile) + sizeof(".lock"); |
67 | lo = malloc(losz); | |
68 | if (!lo) | |
66c33aab | 69 | goto err; |
5eb00eb4 | 70 | |
61394559 | 71 | snprintf(lo, losz, "%s.lock", datafile); |
5eb00eb4 | 72 | |
68164f6c | 73 | ml = calloc(1, sizeof(*ml) ); |
66c33aab KZ |
74 | if (!ml) |
75 | goto err; | |
76 | ||
87938635 | 77 | ml->refcount = 1; |
66c33aab | 78 | ml->lockfile_fd = -1; |
66c33aab | 79 | ml->lockfile = lo; |
3f31a959 | 80 | |
61394559 | 81 | DBG(LOCKS, ul_debugobj(ml, "alloc: lockfile=%s", lo)); |
27c6d415 | 82 | return ml; |
66c33aab KZ |
83 | err: |
84 | free(lo); | |
66c33aab KZ |
85 | free(ml); |
86 | return NULL; | |
27c6d415 KZ |
87 | } |
88 | ||
66c33aab | 89 | |
27c6d415 KZ |
90 | /** |
91 | * mnt_free_lock: | |
68164f6c | 92 | * @ml: struct libmnt_lock handler |
27c6d415 | 93 | * |
87938635 KZ |
94 | * Deallocates libmnt_lock. This function does not care about reference count. Don't |
95 | * use this function directly -- it's better to use mnt_unref_lock(). | |
96 | * | |
97 | * The reference counting is supported since util-linux v2.40. | |
27c6d415 | 98 | */ |
68164f6c | 99 | void mnt_free_lock(struct libmnt_lock *ml) |
27c6d415 KZ |
100 | { |
101 | if (!ml) | |
102 | return; | |
87938635 KZ |
103 | |
104 | DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%d]", | |
105 | ml->locked ? " !!! LOCKED !!!" : "", | |
106 | ml->refcount)); | |
27c6d415 | 107 | free(ml->lockfile); |
27c6d415 KZ |
108 | free(ml); |
109 | } | |
110 | ||
87938635 KZ |
111 | /** |
112 | * mnt_ref_lock: | |
113 | * @ml: lock pointer | |
114 | * | |
115 | * Increments reference counter. | |
116 | * | |
117 | * Since: 2.40 | |
118 | */ | |
119 | void mnt_ref_lock(struct libmnt_lock *ml) | |
120 | { | |
121 | if (ml) { | |
122 | ml->refcount++; | |
123 | /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/ | |
124 | } | |
125 | } | |
126 | ||
127 | /** | |
128 | * mnt_unref_lock: | |
129 | * @ml: lock pointer | |
130 | * | |
131 | * De-increments reference counter, on zero the @ml is automatically | |
132 | * deallocated by mnt_free_lock). | |
133 | */ | |
134 | void mnt_unref_lock(struct libmnt_lock *ml) | |
135 | { | |
136 | if (ml) { | |
137 | ml->refcount--; | |
138 | /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/ | |
139 | if (ml->refcount <= 0) | |
140 | mnt_free_lock(ml); | |
141 | } | |
142 | } | |
143 | ||
5976114f KZ |
144 | /** |
145 | * mnt_lock_block_signals: | |
146 | * @ml: struct libmnt_lock handler | |
147 | * @enable: TRUE/FALSE | |
148 | * | |
149 | * Block/unblock signals when the lock is locked, the signals are not blocked | |
150 | * by default. | |
151 | * | |
152 | * Returns: <0 on error, 0 on success. | |
153 | */ | |
154 | int mnt_lock_block_signals(struct libmnt_lock *ml, int enable) | |
155 | { | |
156 | if (!ml) | |
157 | return -EINVAL; | |
83a78332 | 158 | DBG(LOCKS, ul_debugobj(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED")); |
6f5788c5 | 159 | ml->sigblock = enable ? 1 : 0; |
5976114f KZ |
160 | return 0; |
161 | } | |
162 | ||
0f32f1e2 KZ |
163 | /* |
164 | * Returns path to lockfile. | |
27c6d415 | 165 | */ |
68164f6c | 166 | static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml) |
27c6d415 | 167 | { |
66c33aab | 168 | return ml ? ml->lockfile : NULL; |
27c6d415 KZ |
169 | } |
170 | ||
5eb00eb4 KZ |
171 | /* |
172 | * Simple flocking | |
173 | */ | |
174 | static void unlock_simplelock(struct libmnt_lock *ml) | |
175 | { | |
176 | assert(ml); | |
5eb00eb4 KZ |
177 | |
178 | if (ml->lockfile_fd >= 0) { | |
83a78332 | 179 | DBG(LOCKS, ul_debugobj(ml, "%s: unflocking", |
5eb00eb4 KZ |
180 | mnt_lock_get_lockfile(ml))); |
181 | close(ml->lockfile_fd); | |
182 | } | |
183 | } | |
184 | ||
185 | static int lock_simplelock(struct libmnt_lock *ml) | |
186 | { | |
187 | const char *lfile; | |
188 | int rc; | |
64cb2da8 | 189 | struct stat sb; |
c14bee4d | 190 | const mode_t lock_mask = S_IRUSR|S_IWUSR; |
5eb00eb4 KZ |
191 | |
192 | assert(ml); | |
5eb00eb4 KZ |
193 | |
194 | lfile = mnt_lock_get_lockfile(ml); | |
195 | ||
83a78332 | 196 | DBG(LOCKS, ul_debugobj(ml, "%s: locking", lfile)); |
5eb00eb4 KZ |
197 | |
198 | if (ml->sigblock) { | |
199 | sigset_t sigs; | |
200 | sigemptyset(&ml->oldsigmask); | |
201 | sigfillset(&sigs); | |
202 | sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask); | |
203 | } | |
204 | ||
c14bee4d | 205 | ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, lock_mask); |
5eb00eb4 KZ |
206 | if (ml->lockfile_fd < 0) { |
207 | rc = -errno; | |
208 | goto err; | |
209 | } | |
64cb2da8 TA |
210 | |
211 | rc = fstat(ml->lockfile_fd, &sb); | |
ecfeae90 SB |
212 | if (rc < 0) { |
213 | rc = -errno; | |
214 | goto err; | |
215 | } | |
5eb00eb4 | 216 | |
64cb2da8 TA |
217 | if ((sb.st_mode & lock_mask) != lock_mask) { |
218 | rc = fchmod(ml->lockfile_fd, lock_mask); | |
219 | if (rc < 0) { | |
220 | rc = -errno; | |
221 | goto err; | |
222 | } | |
223 | } | |
224 | ||
5eb00eb4 KZ |
225 | while (flock(ml->lockfile_fd, LOCK_EX) < 0) { |
226 | int errsv; | |
227 | if ((errno == EAGAIN) || (errno == EINTR)) | |
228 | continue; | |
229 | errsv = errno; | |
230 | close(ml->lockfile_fd); | |
231 | ml->lockfile_fd = -1; | |
232 | rc = -errsv; | |
233 | goto err; | |
234 | } | |
86cd5870 | 235 | ml->locked = 1; |
5eb00eb4 KZ |
236 | return 0; |
237 | err: | |
238 | if (ml->sigblock) | |
239 | sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL); | |
240 | return rc; | |
241 | } | |
242 | ||
5eb00eb4 KZ |
243 | /** |
244 | * mnt_lock_file | |
245 | * @ml: pointer to struct libmnt_lock instance | |
246 | * | |
61394559 KZ |
247 | * Creates a lock file. |
248 | * | |
398f7f9d | 249 | * Note that when the lock is used by mnt_update_table() interface then libmount |
61394559 | 250 | * uses flock() for private library file /run/mount/utab. |
398f7f9d | 251 | * |
5eb00eb4 KZ |
252 | * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case |
253 | * of stale lock file). | |
254 | */ | |
255 | int mnt_lock_file(struct libmnt_lock *ml) | |
256 | { | |
257 | if (!ml) | |
258 | return -EINVAL; | |
259 | ||
61394559 | 260 | return lock_simplelock(ml); |
5eb00eb4 KZ |
261 | } |
262 | ||
263 | /** | |
264 | * mnt_unlock_file: | |
265 | * @ml: lock struct | |
266 | * | |
d58b3157 | 267 | * Unlocks the file. The function could be called independently of the |
5eb00eb4 KZ |
268 | * lock status (for example from exit(3)). |
269 | */ | |
270 | void mnt_unlock_file(struct libmnt_lock *ml) | |
271 | { | |
272 | if (!ml) | |
273 | return; | |
274 | ||
83a78332 | 275 | DBG(LOCKS, ul_debugobj(ml, "(%d) %s", getpid(), |
5eb00eb4 KZ |
276 | ml->locked ? "unlocking" : "cleaning")); |
277 | ||
61394559 | 278 | unlock_simplelock(ml); |
5eb00eb4 KZ |
279 | |
280 | ml->locked = 0; | |
281 | ml->lockfile_fd = -1; | |
282 | ||
283 | if (ml->sigblock) { | |
83a78332 | 284 | DBG(LOCKS, ul_debugobj(ml, "restoring sigmask")); |
5eb00eb4 KZ |
285 | sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL); |
286 | } | |
287 | } | |
288 | ||
27c6d415 | 289 | #ifdef TEST_PROGRAM |
27c6d415 | 290 | |
41427f97 | 291 | static struct libmnt_lock *lock; |
27c6d415 KZ |
292 | |
293 | /* | |
294 | * read number from @filename, increment the number and | |
295 | * write the number back to the file | |
296 | */ | |
5fde1d9f | 297 | static void increment_data(const char *filename, int verbose, int loopno) |
27c6d415 KZ |
298 | { |
299 | long num; | |
300 | FILE *f; | |
301 | char buf[256]; | |
302 | ||
1eb8539d | 303 | if (!(f = fopen(filename, "r" UL_CLOEXECSTR))) |
27c6d415 KZ |
304 | err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); |
305 | ||
306 | if (!fgets(buf, sizeof(buf), f)) | |
307 | err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename); | |
308 | ||
309 | fclose(f); | |
310 | num = atol(buf) + 1; | |
311 | ||
1eb8539d | 312 | if (!(f = fopen(filename, "w" UL_CLOEXECSTR))) |
27c6d415 KZ |
313 | err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); |
314 | ||
315 | fprintf(f, "%ld", num); | |
69f1cad4 SK |
316 | |
317 | if (close_stream(f) != 0) | |
318 | err(EXIT_FAILURE, "write failed: %s", filename); | |
27c6d415 KZ |
319 | |
320 | if (verbose) | |
321 | fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(), | |
322 | filename, num - 1, num, loopno); | |
323 | } | |
324 | ||
5fde1d9f | 325 | static void clean_lock(void) |
27c6d415 | 326 | { |
27c6d415 KZ |
327 | if (!lock) |
328 | return; | |
329 | mnt_unlock_file(lock); | |
87938635 | 330 | mnt_unref_lock(lock); |
27c6d415 KZ |
331 | } |
332 | ||
5fde1d9f | 333 | static void __attribute__((__noreturn__)) sig_handler(int sig) |
27c6d415 KZ |
334 | { |
335 | errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig)); | |
336 | } | |
337 | ||
2a472ae8 TW |
338 | static int test_lock(struct libmnt_test *ts __attribute__((unused)), |
339 | int argc, char *argv[]) | |
27c6d415 | 340 | { |
9e10ad3d KZ |
341 | time_t synctime = 0; |
342 | unsigned int usecs; | |
9e10ad3d KZ |
343 | const char *datafile = NULL; |
344 | int verbose = 0, loops = 0, l, idx = 1; | |
27c6d415 | 345 | |
66c33aab | 346 | if (argc < 3) |
9e10ad3d | 347 | return -EINVAL; |
27c6d415 | 348 | |
9e10ad3d KZ |
349 | if (strcmp(argv[idx], "--synctime") == 0) { |
350 | synctime = (time_t) atol(argv[idx + 1]); | |
351 | idx += 2; | |
352 | } | |
353 | if (idx < argc && strcmp(argv[idx], "--verbose") == 0) { | |
27c6d415 | 354 | verbose = 1; |
9e10ad3d KZ |
355 | idx++; |
356 | } | |
357 | ||
358 | if (idx < argc) | |
359 | datafile = argv[idx++]; | |
360 | if (idx < argc) | |
361 | loops = atoi(argv[idx++]); | |
362 | ||
363 | if (!datafile || !loops) | |
364 | return -EINVAL; | |
365 | ||
3a5b1b1d KZ |
366 | if (verbose) |
367 | fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n", | |
368 | getpid(), (int) synctime, datafile, loops); | |
27c6d415 KZ |
369 | |
370 | atexit(clean_lock); | |
371 | ||
372 | /* be paranoid and call exit() (=clean_lock()) for all signals */ | |
373 | { | |
374 | int sig = 0; | |
375 | struct sigaction sa; | |
376 | ||
377 | sa.sa_handler = sig_handler; | |
378 | sa.sa_flags = 0; | |
379 | sigfillset(&sa.sa_mask); | |
380 | ||
381 | while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD) | |
382 | sigaction (sig, &sa, (struct sigaction *) 0); | |
383 | } | |
384 | ||
9e10ad3d KZ |
385 | /* start the test in exactly defined time */ |
386 | if (synctime) { | |
bbf0ff70 KZ |
387 | struct timeval tv; |
388 | ||
389 | gettimeofday(&tv, NULL); | |
9e10ad3d KZ |
390 | if (synctime && synctime - tv.tv_sec > 1) { |
391 | usecs = ((synctime - tv.tv_sec) * 1000000UL) - | |
392 | (1000000UL - tv.tv_usec); | |
a5bd7939 | 393 | xusleep(usecs); |
9e10ad3d KZ |
394 | } |
395 | } | |
396 | ||
27c6d415 | 397 | for (l = 0; l < loops; l++) { |
66c33aab KZ |
398 | lock = mnt_new_lock(datafile, 0); |
399 | if (!lock) | |
400 | return -1; | |
27c6d415 | 401 | |
abc3d154 | 402 | if (mnt_lock_file(lock) != 0) { |
66c33aab KZ |
403 | fprintf(stderr, "%d: failed to lock %s file\n", |
404 | getpid(), datafile); | |
27c6d415 KZ |
405 | return -1; |
406 | } | |
407 | ||
408 | increment_data(datafile, verbose, l); | |
409 | ||
410 | mnt_unlock_file(lock); | |
87938635 | 411 | mnt_unref_lock(lock); |
27c6d415 | 412 | lock = NULL; |
9e10ad3d | 413 | |
d58b3157 | 414 | /* The mount command usually finishes after a mtab update. We |
9e10ad3d KZ |
415 | * simulate this via short sleep -- it's also enough to make |
416 | * concurrent processes happy. | |
417 | */ | |
418 | if (synctime) | |
a5bd7939 | 419 | xusleep(25000); |
27c6d415 KZ |
420 | } |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
192c6aad KZ |
425 | /* |
426 | * Note that this test should be executed from a script that creates many | |
427 | * parallel processes, otherwise this test does not make sense. | |
428 | */ | |
27c6d415 KZ |
429 | int main(int argc, char *argv[]) |
430 | { | |
68164f6c | 431 | struct libmnt_test tss[] = { |
9e10ad3d KZ |
432 | { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> " |
433 | "increment a number in datafile" }, | |
27c6d415 KZ |
434 | { NULL } |
435 | }; | |
436 | ||
437 | return mnt_run_test(tss, argc, argv); | |
438 | } | |
439 | ||
440 | #endif /* TEST_PROGRAM */ |