]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/lock.c
28ff8b788b6c3b2417da1e07dcafae2a00f8b206
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2009-2018 Karel Zak <kzak@redhat.com>
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.
16 * @short_description: locking methods for utab or another libmount files
18 * Since v2.39 libmount does not support classic mtab locking. Now all is based
30 #include "closestream.h"
31 #include "pathnames.h"
33 #include "monotonic.h"
39 int refcount
; /* reference counter */
40 char *lockfile
; /* path to lock file (e.g. /etc/mtab~) */
41 int lockfile_fd
; /* lock file descriptor */
43 bool locked
, /* do we own the lock? */
44 sigblock
; /* block signals when locked */
52 * @datafile: the file that should be covered by the lock
53 * @id: ignored by library
55 * Returns: newly allocated lock handler or NULL on case of error.
57 struct libmnt_lock
*mnt_new_lock(const char *datafile
, pid_t id
__attribute__((__unused__
)))
59 struct libmnt_lock
*ml
= NULL
;
66 losz
= strlen(datafile
) + sizeof(".lock");
71 snprintf(lo
, losz
, "%s.lock", datafile
);
73 ml
= calloc(1, sizeof(*ml
) );
81 DBG(LOCKS
, ul_debugobj(ml
, "alloc: lockfile=%s", lo
));
92 * @ml: struct libmnt_lock handler
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().
97 * The reference counting is supported since util-linux v2.40.
99 void mnt_free_lock(struct libmnt_lock
*ml
)
104 DBG(LOCKS
, ul_debugobj(ml
, "free%s [refcount=%d]",
105 ml
->locked
? " !!! LOCKED !!!" : "",
115 * Increments reference counter.
119 void mnt_ref_lock(struct libmnt_lock
*ml
)
123 /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/
131 * De-increments reference counter, on zero the @ml is automatically
132 * deallocated by mnt_free_lock).
134 void mnt_unref_lock(struct libmnt_lock
*ml
)
138 /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/
139 if (ml
->refcount
<= 0)
145 * mnt_lock_block_signals:
146 * @ml: struct libmnt_lock handler
147 * @enable: TRUE/FALSE
149 * Block/unblock signals when the lock is locked, the signals are not blocked
152 * Returns: <0 on error, 0 on success.
154 int mnt_lock_block_signals(struct libmnt_lock
*ml
, int enable
)
158 DBG(LOCKS
, ul_debugobj(ml
, "signals: %s", enable
? "BLOCKED" : "UNBLOCKED"));
159 ml
->sigblock
= enable
? 1 : 0;
164 * Returns path to lockfile.
166 static const char *mnt_lock_get_lockfile(struct libmnt_lock
*ml
)
168 return ml
? ml
->lockfile
: NULL
;
174 static void unlock_simplelock(struct libmnt_lock
*ml
)
178 if (ml
->lockfile_fd
>= 0) {
179 DBG(LOCKS
, ul_debugobj(ml
, "%s: unflocking",
180 mnt_lock_get_lockfile(ml
)));
181 close(ml
->lockfile_fd
);
185 static int lock_simplelock(struct libmnt_lock
*ml
)
190 const mode_t lock_mask
= S_IRUSR
|S_IWUSR
;
194 lfile
= mnt_lock_get_lockfile(ml
);
196 DBG(LOCKS
, ul_debugobj(ml
, "%s: locking", lfile
));
200 sigemptyset(&ml
->oldsigmask
);
202 sigprocmask(SIG_BLOCK
, &sigs
, &ml
->oldsigmask
);
205 ml
->lockfile_fd
= open(lfile
, O_RDONLY
|O_CREAT
|O_CLOEXEC
, lock_mask
);
206 if (ml
->lockfile_fd
< 0) {
211 rc
= fstat(ml
->lockfile_fd
, &sb
);
217 if ((sb
.st_mode
& lock_mask
) != lock_mask
) {
218 rc
= fchmod(ml
->lockfile_fd
, lock_mask
);
225 while (flock(ml
->lockfile_fd
, LOCK_EX
) < 0) {
227 if ((errno
== EAGAIN
) || (errno
== EINTR
))
230 close(ml
->lockfile_fd
);
231 ml
->lockfile_fd
= -1;
239 sigprocmask(SIG_SETMASK
, &ml
->oldsigmask
, NULL
);
245 * @ml: pointer to struct libmnt_lock instance
247 * Creates a lock file.
249 * Note that when the lock is used by mnt_update_table() interface then libmount
250 * uses flock() for private library file /run/mount/utab.
252 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
253 * of stale lock file).
255 int mnt_lock_file(struct libmnt_lock
*ml
)
260 return lock_simplelock(ml
);
267 * Unlocks the file. The function could be called independently of the
268 * lock status (for example from exit(3)).
270 void mnt_unlock_file(struct libmnt_lock
*ml
)
275 DBG(LOCKS
, ul_debugobj(ml
, "(%d) %s", getpid(),
276 ml
->locked
? "unlocking" : "cleaning"));
278 unlock_simplelock(ml
);
281 ml
->lockfile_fd
= -1;
284 DBG(LOCKS
, ul_debugobj(ml
, "restoring sigmask"));
285 sigprocmask(SIG_SETMASK
, &ml
->oldsigmask
, NULL
);
291 static struct libmnt_lock
*lock
;
294 * read number from @filename, increment the number and
295 * write the number back to the file
297 static void increment_data(const char *filename
, int verbose
, int loopno
)
303 if (!(f
= fopen(filename
, "r" UL_CLOEXECSTR
)))
304 err(EXIT_FAILURE
, "%d: failed to open: %s", getpid(), filename
);
306 if (!fgets(buf
, sizeof(buf
), f
))
307 err(EXIT_FAILURE
, "%d failed read: %s", getpid(), filename
);
312 if (!(f
= fopen(filename
, "w" UL_CLOEXECSTR
)))
313 err(EXIT_FAILURE
, "%d: failed to open: %s", getpid(), filename
);
315 fprintf(f
, "%ld", num
);
317 if (close_stream(f
) != 0)
318 err(EXIT_FAILURE
, "write failed: %s", filename
);
321 fprintf(stderr
, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
322 filename
, num
- 1, num
, loopno
);
325 static void clean_lock(void)
329 mnt_unlock_file(lock
);
330 mnt_unref_lock(lock
);
333 static void __attribute__((__noreturn__
)) sig_handler(int sig
)
335 errx(EXIT_FAILURE
, "\n%d: catch signal: %s\n", getpid(), strsignal(sig
));
338 static int test_lock(struct libmnt_test
*ts
__attribute__((unused
)),
339 int argc
, char *argv
[])
343 const char *datafile
= NULL
;
344 int verbose
= 0, loops
= 0, l
, idx
= 1;
349 if (strcmp(argv
[idx
], "--synctime") == 0) {
350 synctime
= (time_t) atol(argv
[idx
+ 1]);
353 if (idx
< argc
&& strcmp(argv
[idx
], "--verbose") == 0) {
359 datafile
= argv
[idx
++];
361 loops
= atoi(argv
[idx
++]);
363 if (!datafile
|| !loops
)
367 fprintf(stderr
, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
368 getpid(), (int) synctime
, datafile
, loops
);
372 /* be paranoid and call exit() (=clean_lock()) for all signals */
377 sa
.sa_handler
= sig_handler
;
379 sigfillset(&sa
.sa_mask
);
381 while (sigismember(&sa
.sa_mask
, ++sig
) != -1 && sig
!= SIGCHLD
)
382 sigaction (sig
, &sa
, (struct sigaction
*) 0);
385 /* start the test in exactly defined time */
389 gettimeofday(&tv
, NULL
);
390 if (synctime
&& synctime
- tv
.tv_sec
> 1) {
391 usecs
= ((synctime
- tv
.tv_sec
) * 1000000UL) -
392 (1000000UL - tv
.tv_usec
);
397 for (l
= 0; l
< loops
; l
++) {
398 lock
= mnt_new_lock(datafile
, 0);
402 if (mnt_lock_file(lock
) != 0) {
403 fprintf(stderr
, "%d: failed to lock %s file\n",
408 increment_data(datafile
, verbose
, l
);
410 mnt_unlock_file(lock
);
411 mnt_unref_lock(lock
);
414 /* The mount command usually finishes after a mtab update. We
415 * simulate this via short sleep -- it's also enough to make
416 * concurrent processes happy.
426 * Note that this test should be executed from a script that creates many
427 * parallel processes, otherwise this test does not make sense.
429 int main(int argc
, char *argv
[])
431 struct libmnt_test tss
[] = {
432 { "--lock", test_lock
, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
433 "increment a number in datafile" },
437 return mnt_run_test(tss
, argc
, argv
);
440 #endif /* TEST_PROGRAM */