]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/lock.c
2 * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
11 * @short_description: locking methods for /etc/mtab or another libmount files
13 * The mtab lock is backwards compatible with the standard linux /etc/mtab
14 * locking. Note, it's necessary to use the same locking schema in all
15 * applications that access the file.
25 #include "closestream.h"
26 #include "pathnames.h"
28 #include "monotonic.h"
34 char *lockfile
; /* path to lock file (e.g. /etc/mtab~) */
35 char *linkfile
; /* path to link file (e.g. /etc/mtab~.<id>) */
36 int lockfile_fd
; /* lock file descriptor */
38 unsigned int locked
:1, /* do we own the lock? */
39 sigblock
:1, /* block signals when locked */
40 simplelock
:1; /* use flock rather than normal mtab lock */
48 * @datafile: the file that should be covered by the lock
49 * @id: unique linkfile identifier or 0 (default is getpid())
51 * Returns: newly allocated lock handler or NULL on case of error.
53 struct libmnt_lock
*mnt_new_lock(const char *datafile
, pid_t id
)
55 struct libmnt_lock
*ml
= NULL
;
56 char *lo
= NULL
, *ln
= NULL
;
61 /* for flock we use "foo.lock, for mtab "foo~"
63 losz
= strlen(datafile
) + sizeof(".lock");
68 /* default is mtab~ lock */
69 snprintf(lo
, losz
, "%s~", datafile
);
71 if (asprintf(&ln
, "%s~.%d", datafile
, id
? : getpid()) == -1) {
75 ml
= calloc(1, sizeof(*ml
) );
83 DBG(LOCKS
, ul_debugobj(ml
, "alloc: default linkfile=%s, lockfile=%s", ln
, lo
));
95 * @ml: struct libmnt_lock handler
97 * Deallocates mnt_lock.
99 void mnt_free_lock(struct libmnt_lock
*ml
)
103 DBG(LOCKS
, ul_debugobj(ml
, "free%s", ml
->locked
? " !!! LOCKED !!!" : ""));
110 * mnt_lock_block_signals:
111 * @ml: struct libmnt_lock handler
112 * @enable: TRUE/FALSE
114 * Block/unblock signals when the lock is locked, the signals are not blocked
117 * Returns: <0 on error, 0 on success.
119 int mnt_lock_block_signals(struct libmnt_lock
*ml
, int enable
)
123 DBG(LOCKS
, ul_debugobj(ml
, "signals: %s", enable
? "BLOCKED" : "UNBLOCKED"));
124 ml
->sigblock
= enable
? 1 : 0;
128 /* don't export this to API
130 int mnt_lock_use_simplelock(struct libmnt_lock
*ml
, int enable
)
137 assert(ml
->lockfile
);
139 DBG(LOCKS
, ul_debugobj(ml
, "flock: %s", enable
? "ENABLED" : "DISABLED"));
140 ml
->simplelock
= enable
? 1 : 0;
142 sz
= strlen(ml
->lockfile
);
150 * flock: "<name>.lock"
151 * mtab lock: "<name>~"
153 if (ml
->simplelock
&& endswith(ml
->lockfile
, "~"))
154 memcpy(ml
->lockfile
+ sz
- 1, ".lock", 6);
156 else if (!ml
->simplelock
&& endswith(ml
->lockfile
, ".lock"))
157 memcpy(ml
->lockfile
+ sz
- 5, "~", 2);
159 DBG(LOCKS
, ul_debugobj(ml
, "new lock filename: '%s'", ml
->lockfile
));
164 * Returns path to lockfile.
166 static const char *mnt_lock_get_lockfile(struct libmnt_lock
*ml
)
168 return ml
? ml
->lockfile
: NULL
;
172 * Note that the filename is generated by mnt_new_lock() and depends on
173 * getpid() or 'id' argument of the mnt_new_lock() function.
175 * Returns: unique (per process/thread) path to linkfile.
177 static const char *mnt_lock_get_linkfile(struct libmnt_lock
*ml
)
179 return ml
? ml
->linkfile
: NULL
;
185 static void unlock_simplelock(struct libmnt_lock
*ml
)
188 assert(ml
->simplelock
);
190 if (ml
->lockfile_fd
>= 0) {
191 DBG(LOCKS
, ul_debugobj(ml
, "%s: unflocking",
192 mnt_lock_get_lockfile(ml
)));
193 close(ml
->lockfile_fd
);
197 static int lock_simplelock(struct libmnt_lock
*ml
)
203 assert(ml
->simplelock
);
205 lfile
= mnt_lock_get_lockfile(ml
);
207 DBG(LOCKS
, ul_debugobj(ml
, "%s: locking", lfile
));
211 sigemptyset(&ml
->oldsigmask
);
213 sigprocmask(SIG_BLOCK
, &sigs
, &ml
->oldsigmask
);
216 ml
->lockfile_fd
= open(lfile
, O_RDONLY
|O_CREAT
|O_CLOEXEC
,
217 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
218 if (ml
->lockfile_fd
< 0) {
223 while (flock(ml
->lockfile_fd
, LOCK_EX
) < 0) {
225 if ((errno
== EAGAIN
) || (errno
== EINTR
))
228 close(ml
->lockfile_fd
);
229 ml
->lockfile_fd
= -1;
237 sigprocmask(SIG_SETMASK
, &ml
->oldsigmask
, NULL
);
242 * traditional mtab locking
245 static void mnt_lockalrm_handler(int sig
__attribute__((__unused__
)))
247 /* do nothing, say nothing, be nothing */
251 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
252 * fcntl() to avoid neverending waiting.
254 * Returns: 0 on success, 1 on timeout, -errno on error.
256 static int mnt_wait_mtab_lock(struct libmnt_lock
*ml
, struct flock
*fl
, time_t maxtime
)
259 struct sigaction sa
, osa
;
262 gettime_monotonic(&now
);
263 DBG(LOCKS
, ul_debugobj(ml
, "(%d) waiting for F_SETLKW (now=%lu, maxtime=%lu, diff=%lu)",
265 (unsigned long) now
.tv_sec
,
266 (unsigned long) maxtime
,
267 (unsigned long) (maxtime
- now
.tv_sec
)));
269 if (now
.tv_sec
>= maxtime
)
270 return 1; /* timeout */
272 /* setup ALARM handler -- we don't want to wait forever */
274 sa
.sa_handler
= mnt_lockalrm_handler
;
275 sigfillset (&sa
.sa_mask
);
277 sigaction(SIGALRM
, &sa
, &osa
);
280 alarm(maxtime
- now
.tv_sec
);
281 if (fcntl(ml
->lockfile_fd
, F_SETLKW
, fl
) == -1)
282 ret
= errno
== EINTR
? 1 : -errno
;
285 /* restore old sigaction */
286 sigaction(SIGALRM
, &osa
, NULL
);
288 DBG(LOCKS
, ul_debugobj(ml
, "(%d) leaving mnt_wait_setlkw(), rc=%d",
294 * Create the mtab lock file.
296 * The old code here used flock on a lock file /etc/mtab~ and deleted
297 * this lock file afterwards. However, as rgooch remarks, that has a
298 * race: a second mount may be waiting on the lock and proceed as
299 * soon as the lock file is deleted by the first mount, and immediately
300 * afterwards a third mount comes, creates a new /etc/mtab~, applies
301 * flock to that, and also proceeds, so that the second and third mount
302 * are now both scribbling in /etc/mtab.
304 * The new code uses a link() instead of a creat(), where we proceed
305 * only if it was us that created the lock, and hence we always have
306 * to delete the lock afterwards. Now the use of flock() is in principle
307 * superfluous, but avoids an arbitrary sleep().
309 * Where does the link point to? Obvious choices are mtab and mtab~~.
310 * HJLu points out that the latter leads to races. Right now we use
314 * The original mount locking code has used sleep(1) between attempts and
315 * maximal number of attempts has been 5.
317 * There was a very small number of attempts and extremely long waiting (1s)
318 * that is useless on machines with large number of mount processes.
320 * Now we wait for a few thousand microseconds between attempts and we have a global
321 * time limit (30s) rather than a limit for the number of attempts. The advantage
322 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
323 * the number of attempts is not restricted.
324 * -- kzak@redhat.com [Mar-2007]
327 * This mtab locking code has been refactored and moved to libmount. The mtab
328 * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and
329 * backwardly compatible code.
331 * Don't forget that this code has to be compatible with 3rd party mounts
332 * (/sbin/mount.foo) and has to work with NFS.
333 * -- kzak@redhat.com [May-2009]
336 /* maximum seconds between the first and the last attempt */
337 #define MOUNTLOCK_MAXTIME 30
339 /* sleep time (in microseconds, max=999999) between attempts */
340 #define MOUNTLOCK_WAITTIME 5000
342 static void unlock_mtab(struct libmnt_lock
*ml
)
347 if (!ml
->locked
&& ml
->lockfile
&& ml
->linkfile
)
349 /* We (probably) have all the files, but we don't own the lock,
350 * Really? Check it! Maybe ml->locked wasn't set properly
351 * because the code was interrupted by a signal. Paranoia? Yes.
353 * We own the lock when linkfile == lockfile.
357 if (!stat(ml
->lockfile
, &lo
) && !stat(ml
->linkfile
, &li
) &&
358 lo
.st_dev
== li
.st_dev
&& lo
.st_ino
== li
.st_ino
)
363 unlink(ml
->linkfile
);
364 if (ml
->lockfile_fd
>= 0)
365 close(ml
->lockfile_fd
);
366 if (ml
->locked
&& ml
->lockfile
) {
367 unlink(ml
->lockfile
);
368 DBG(LOCKS
, ul_debugobj(ml
, "unlink %s", ml
->lockfile
));
372 static int lock_mtab(struct libmnt_lock
*ml
)
375 struct timespec waittime
;
376 struct timeval maxtime
;
377 const char *lockfile
, *linkfile
;
384 lockfile
= mnt_lock_get_lockfile(ml
);
387 linkfile
= mnt_lock_get_linkfile(ml
);
393 * Block all signals when locked, mnt_unlock_file() will
394 * restore the old mask.
398 sigemptyset(&ml
->oldsigmask
);
400 sigdelset(&sigs
, SIGTRAP
);
401 sigdelset(&sigs
, SIGALRM
);
402 sigprocmask(SIG_BLOCK
, &sigs
, &ml
->oldsigmask
);
405 i
= open(linkfile
, O_WRONLY
|O_CREAT
|O_CLOEXEC
, S_IRUSR
|S_IWUSR
);
407 /* linkfile does not exist (as a file) and we cannot create it.
408 * Read-only or full filesystem? Too many files open in the system?
416 gettime_monotonic(&maxtime
);
417 maxtime
.tv_sec
+= MOUNTLOCK_MAXTIME
;
420 waittime
.tv_nsec
= (1000 * MOUNTLOCK_WAITTIME
);
422 /* Repeat until it was us who made the link */
423 while (!ml
->locked
) {
428 j
= link(linkfile
, lockfile
);
432 if (j
< 0 && errno
!= EEXIST
) {
437 ml
->lockfile_fd
= open(lockfile
, O_WRONLY
|O_CLOEXEC
);
439 if (ml
->lockfile_fd
< 0) {
440 /* Strange... Maybe the file was just deleted? */
442 gettime_monotonic(&now
);
443 if (errsv
== ENOENT
&& now
.tv_sec
< maxtime
.tv_sec
) {
452 flock
.l_type
= F_WRLCK
;
453 flock
.l_whence
= SEEK_SET
;
458 /* We made the link. Now claim the lock. */
459 if (fcntl (ml
->lockfile_fd
, F_SETLK
, &flock
) == -1) {
460 DBG(LOCKS
, ul_debugobj(ml
,
461 "%s: can't F_SETLK lockfile, errno=%d\n",
463 /* proceed, since it was us who created the lockfile anyway */
467 /* Someone else made the link. Wait. */
468 int err
= mnt_wait_mtab_lock(ml
, &flock
, maxtime
.tv_sec
);
471 DBG(LOCKS
, ul_debugobj(ml
,
472 "%s: can't create link: time out (perhaps "
473 "there is a stale lock file?)", lockfile
));
477 } else if (err
< 0) {
481 nanosleep(&waittime
, NULL
);
482 close(ml
->lockfile_fd
);
483 ml
->lockfile_fd
= -1;
486 DBG(LOCKS
, ul_debugobj(ml
, "%s: (%d) successfully locked",
487 lockfile
, getpid()));
499 * @ml: pointer to struct libmnt_lock instance
501 * Creates a lock file (e.g. /etc/mtab~). Note that this function may
504 * Your application always has to call mnt_unlock_file() before exit.
506 * Traditional mtab locking scheme:
508 * 1. create linkfile (e.g. /etc/mtab~.$PID)
509 * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
510 * 3. a) link() success: setups F_SETLK lock (see fcnlt(2))
511 * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2.
513 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
514 * of stale lock file).
516 int mnt_lock_file(struct libmnt_lock
*ml
)
522 return lock_simplelock(ml
);
524 return lock_mtab(ml
);
531 * Unlocks the file. The function could be called independently of the
532 * lock status (for example from exit(3)).
534 void mnt_unlock_file(struct libmnt_lock
*ml
)
539 DBG(LOCKS
, ul_debugobj(ml
, "(%d) %s", getpid(),
540 ml
->locked
? "unlocking" : "cleaning"));
543 unlock_simplelock(ml
);
548 ml
->lockfile_fd
= -1;
551 DBG(LOCKS
, ul_debugobj(ml
, "restoring sigmask"));
552 sigprocmask(SIG_SETMASK
, &ml
->oldsigmask
, NULL
);
558 struct libmnt_lock
*lock
;
561 * read number from @filename, increment the number and
562 * write the number back to the file
564 void increment_data(const char *filename
, int verbose
, int loopno
)
570 if (!(f
= fopen(filename
, "r" UL_CLOEXECSTR
)))
571 err(EXIT_FAILURE
, "%d: failed to open: %s", getpid(), filename
);
573 if (!fgets(buf
, sizeof(buf
), f
))
574 err(EXIT_FAILURE
, "%d failed read: %s", getpid(), filename
);
579 if (!(f
= fopen(filename
, "w" UL_CLOEXECSTR
)))
580 err(EXIT_FAILURE
, "%d: failed to open: %s", getpid(), filename
);
582 fprintf(f
, "%ld", num
);
584 if (close_stream(f
) != 0)
585 err(EXIT_FAILURE
, "write failed: %s", filename
);
588 fprintf(stderr
, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
589 filename
, num
- 1, num
, loopno
);
592 void clean_lock(void)
596 mnt_unlock_file(lock
);
600 void __attribute__((__noreturn__
)) sig_handler(int sig
)
602 errx(EXIT_FAILURE
, "\n%d: catch signal: %s\n", getpid(), strsignal(sig
));
605 int test_lock(struct libmnt_test
*ts
, int argc
, char *argv
[])
609 const char *datafile
= NULL
;
610 int verbose
= 0, loops
= 0, l
, idx
= 1;
615 if (strcmp(argv
[idx
], "--synctime") == 0) {
616 synctime
= (time_t) atol(argv
[idx
+ 1]);
619 if (idx
< argc
&& strcmp(argv
[idx
], "--verbose") == 0) {
625 datafile
= argv
[idx
++];
627 loops
= atoi(argv
[idx
++]);
629 if (!datafile
|| !loops
)
633 fprintf(stderr
, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
634 getpid(), (int) synctime
, datafile
, loops
);
638 /* be paranoid and call exit() (=clean_lock()) for all signals */
643 sa
.sa_handler
= sig_handler
;
645 sigfillset(&sa
.sa_mask
);
647 while (sigismember(&sa
.sa_mask
, ++sig
) != -1 && sig
!= SIGCHLD
)
648 sigaction (sig
, &sa
, (struct sigaction
*) 0);
651 /* start the test in exactly defined time */
655 gettimeofday(&tv
, NULL
);
656 if (synctime
&& synctime
- tv
.tv_sec
> 1) {
657 usecs
= ((synctime
- tv
.tv_sec
) * 1000000UL) -
658 (1000000UL - tv
.tv_usec
);
663 for (l
= 0; l
< loops
; l
++) {
664 lock
= mnt_new_lock(datafile
, 0);
668 if (mnt_lock_file(lock
) != 0) {
669 fprintf(stderr
, "%d: failed to lock %s file\n",
674 increment_data(datafile
, verbose
, l
);
676 mnt_unlock_file(lock
);
680 /* The mount command usually finishes after a mtab update. We
681 * simulate this via short sleep -- it's also enough to make
682 * concurrent processes happy.
692 * Note that this test should be executed from a script that creates many
693 * parallel processes, otherwise this test does not make sense.
695 int main(int argc
, char *argv
[])
697 struct libmnt_test tss
[] = {
698 { "--lock", test_lock
, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
699 "increment a number in datafile" },
703 return mnt_run_test(tss
, argc
, argv
);
706 #endif /* TEST_PROGRAM */