]>
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 backwardly compatible with the standard linux /etc/mtab
14 * locking. Note, it's necessary to use the same locking schema in all
15 * application that access the file.
19 #include <sys/types.h>
31 #include "pathnames.h"
41 char *lockfile
; /* path to lock file (e.g. /etc/mtab~) */
42 char *linkfile
; /* path to link file (e.g. /etc/mtab~.<id>) */
43 int lockfile_fd
; /* lock file descriptor */
45 int locked
: 1; /* do we own the lock? */
46 int sigblock
: 1; /* block signals when locked */
47 int simplelock
: 1; /* use flock rather than normal mtab lock */
55 * @datafile: the file that should be covered by the lock
56 * @id: unique linkfile identifier or 0 (default is getpid())
58 * Returns: newly allocated lock handler or NULL on case of error.
60 struct libmnt_lock
*mnt_new_lock(const char *datafile
, pid_t id
)
62 struct libmnt_lock
*ml
= NULL
;
63 char *lo
= NULL
, *ln
= NULL
;
69 /* for flock we use "foo.lock, for mtab "foo~"
71 losz
= strlen(datafile
) + sizeof(".lock");
76 /* default is mtab~ lock */
77 snprintf(lo
, losz
, "%s~", datafile
);
79 if (asprintf(&ln
, "%s~.%d", datafile
, id
? : getpid()) == -1) {
83 ml
= calloc(1, sizeof(*ml
) );
91 DBG(LOCKS
, mnt_debug_h(ml
, "alloc: default linkfile=%s, lockfile=%s", ln
, lo
));
103 * @ml: struct libmnt_lock handler
105 * Deallocates mnt_lock.
107 void mnt_free_lock(struct libmnt_lock
*ml
)
111 DBG(LOCKS
, mnt_debug_h(ml
, "free%s", ml
->locked
? " !!! LOCKED !!!" : ""));
118 * mnt_lock_block_signals:
119 * @ml: struct libmnt_lock handler
120 * @enable: TRUE/FALSE
122 * Block/unblock signals when the lock is locked, the signals are not blocked
125 * Returns: <0 on error, 0 on success.
127 int mnt_lock_block_signals(struct libmnt_lock
*ml
, int enable
)
131 DBG(LOCKS
, mnt_debug_h(ml
, "signals: %s", enable
? "BLOCKED" : "UNBLOCKED"));
132 ml
->sigblock
= enable
? 1 : 0;
136 /* don't export this to API
138 int mnt_lock_use_simplelock(struct libmnt_lock
*ml
, int enable
)
145 assert(ml
->lockfile
);
147 DBG(LOCKS
, mnt_debug_h(ml
, "flock: %s", enable
? "ENABLED" : "DISABLED"));
148 ml
->simplelock
= enable
? 1 : 0;
150 sz
= strlen(ml
->lockfile
);
154 * flock: "<name>.lock"
155 * mtab lock: "<name>~"
157 if (ml
->simplelock
&& endswith(ml
->lockfile
, "~"))
158 memcpy(ml
->lockfile
+ sz
- 1, ".lock", 6);
160 else if (!ml
->simplelock
&& endswith(ml
->lockfile
, ".lock"))
161 memcpy(ml
->lockfile
+ sz
- 5, "~", 2);
163 DBG(LOCKS
, mnt_debug_h(ml
, "new lock filename: '%s'", ml
->lockfile
));
168 * Returns path to lockfile.
170 static const char *mnt_lock_get_lockfile(struct libmnt_lock
*ml
)
172 return ml
? ml
->lockfile
: NULL
;
176 * Note that the filename is generated by mnt_new_lock() and depends on
177 * getpid() or 'id' argument of the mnt_new_lock() function.
179 * Returns: unique (per process/thread) path to linkfile.
181 static const char *mnt_lock_get_linkfile(struct libmnt_lock
*ml
)
183 return ml
? ml
->linkfile
: NULL
;
189 static void unlock_simplelock(struct libmnt_lock
*ml
)
192 assert(ml
->simplelock
);
194 if (ml
->lockfile_fd
>= 0) {
195 DBG(LOCKS
, mnt_debug_h(ml
, "%s: unflocking",
196 mnt_lock_get_lockfile(ml
)));
197 close(ml
->lockfile_fd
);
201 static int lock_simplelock(struct libmnt_lock
*ml
)
207 assert(ml
->simplelock
);
209 lfile
= mnt_lock_get_lockfile(ml
);
211 DBG(LOCKS
, mnt_debug_h(ml
, "%s: locking", lfile
));
215 sigemptyset(&ml
->oldsigmask
);
217 sigprocmask(SIG_BLOCK
, &sigs
, &ml
->oldsigmask
);
220 ml
->lockfile_fd
= open(lfile
, O_RDONLY
|O_CREAT
|O_CLOEXEC
,
221 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
222 if (ml
->lockfile_fd
< 0) {
227 while (flock(ml
->lockfile_fd
, LOCK_EX
) < 0) {
229 if ((errno
== EAGAIN
) || (errno
== EINTR
))
232 close(ml
->lockfile_fd
);
233 ml
->lockfile_fd
= -1;
241 sigprocmask(SIG_SETMASK
, &ml
->oldsigmask
, NULL
);
246 * traditional mtab locking
249 static void mnt_lockalrm_handler(int sig
)
251 /* do nothing, say nothing, be nothing */
255 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
256 * fcntl() to avoid never ending waiting.
258 * Returns: 0 on success, 1 on timeout, -errno on error.
260 static int mnt_wait_mtab_lock(struct libmnt_lock
*ml
, struct flock
*fl
, time_t maxtime
)
263 struct sigaction sa
, osa
;
266 gettimeofday(&now
, NULL
);
268 if (now
.tv_sec
>= maxtime
)
269 return 1; /* timeout */
271 /* setup ALARM handler -- we don't want to wait forever */
273 sa
.sa_handler
= mnt_lockalrm_handler
;
274 sigfillset (&sa
.sa_mask
);
276 sigaction(SIGALRM
, &sa
, &osa
);
278 DBG(LOCKS
, mnt_debug_h(ml
, "(%d) waiting for F_SETLKW", getpid()));
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
, mnt_debug_h(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 * now both are 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
311 * mtab~.<pid> instead.
314 * The original mount locking code has used sleep(1) between attempts and
315 * maximal number of attempts has been 5.
317 * There was 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 few thousand microseconds between attempts and we have a global
321 * time limit (30s) rather than limit for number of attempts. The advantage
322 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
323 * 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 first and 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 have (probably) all files, but we don't own the lock,
350 * Really? Check it! Maybe ml->locked wasn't set properly
351 * because code was interrupted by 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
, mnt_debug_h(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
, 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 gettimeofday(&maxtime
, NULL
);
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
);
439 if (ml
->lockfile_fd
< 0) {
440 /* Strange... Maybe the file was just deleted? */
442 gettimeofday(&now
, NULL
);
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
, mnt_debug_h(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
, mnt_debug_h(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
, mnt_debug_h(ml
, "%s: (%d) successfully locked",
487 lockfile
, getpid()));
499 * @ml: pointer to struct libmnt_lock instance
501 * Creates lock file (e.g. /etc/mtab~). Note that this function may
504 * Your application has to always 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 on the
532 * lock status (for example from exit(3)).
534 void mnt_unlock_file(struct libmnt_lock
*ml
)
539 DBG(LOCKS
, mnt_debug_h(ml
, "(%d) %s", getpid(),
540 ml
->locked
? "unlocking" : "cleaning"));
543 unlock_simplelock(ml
);
548 ml
->lockfile_fd
= -1;
551 DBG(LOCKS
, mnt_debug_h(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")))
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")))
580 err(EXIT_FAILURE
, "%d: failed to open: %s", getpid(), filename
);
582 fprintf(f
, "%ld", num
);
586 fprintf(stderr
, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
587 filename
, num
- 1, num
, loopno
);
590 void clean_lock(void)
594 mnt_unlock_file(lock
);
598 void sig_handler(int sig
)
600 errx(EXIT_FAILURE
, "\n%d: catch signal: %s\n", getpid(), strsignal(sig
));
603 int test_lock(struct libmnt_test
*ts
, int argc
, char *argv
[])
608 const char *datafile
= NULL
;
609 int verbose
= 0, loops
= 0, l
, idx
= 1;
614 if (strcmp(argv
[idx
], "--synctime") == 0) {
615 synctime
= (time_t) atol(argv
[idx
+ 1]);
618 if (idx
< argc
&& strcmp(argv
[idx
], "--verbose") == 0) {
624 datafile
= argv
[idx
++];
626 loops
= atoi(argv
[idx
++]);
628 if (!datafile
|| !loops
)
632 fprintf(stderr
, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
633 getpid(), (int) synctime
, datafile
, loops
);
637 /* be paranoid and call exit() (=clean_lock()) for all signals */
642 sa
.sa_handler
= sig_handler
;
644 sigfillset(&sa
.sa_mask
);
646 while (sigismember(&sa
.sa_mask
, ++sig
) != -1 && sig
!= SIGCHLD
)
647 sigaction (sig
, &sa
, (struct sigaction
*) 0);
650 /* start the test in exactly defined time */
652 gettimeofday(&tv
, NULL
);
653 if (synctime
&& synctime
- tv
.tv_sec
> 1) {
654 usecs
= ((synctime
- tv
.tv_sec
) * 1000000UL) -
655 (1000000UL - tv
.tv_usec
);
660 for (l
= 0; l
< loops
; l
++) {
661 lock
= mnt_new_lock(datafile
, 0);
665 if (mnt_lock_file(lock
) != 0) {
666 fprintf(stderr
, "%d: failed to lock %s file\n",
671 increment_data(datafile
, verbose
, l
);
673 mnt_unlock_file(lock
);
677 /* The mount command usually finish after mtab update. We
678 * simulate this via short sleep -- it's also enough to make
679 * concurrent processes happy.
689 * Note that this test should be executed from a script that creates many
690 * parallel processes, otherwise this test does not make sense.
692 int main(int argc
, char *argv
[])
694 struct libmnt_test tss
[] = {
695 { "--lock", test_lock
, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
696 "increment a number in datafile" },
700 return mnt_run_test(tss
, argc
, argv
);
703 #endif /* TEST_PROGRAM */