]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/lock.c
taskset: Accept 0 pid for current process
[thirdparty/util-linux.git] / libmount / src / lock.c
CommitLineData
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 38struct 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 57struct 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
83err:
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 99void 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 */
119void 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 */
134void 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 */
154int 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 166static 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 */
174static 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
185static 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;
237err:
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 */
255int 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 */
270void 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 291static 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 297static 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 325static 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 333static 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
338static 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
429int 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 */