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.
10
* @title: Mtab locking
11
* @short_description: locking methods for work with /etc/mtab
13
* The lock is backwardly compatible with the standard linux /etc/mtab locking.
14
* Note, it's necessary to use the same locking schema in all application that
19
#include <sys/types.h>
30
#include "pathnames.h"
39
char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
40
char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */
41
int lockfile_fd; /* lock file descriptor */
43
int locked : 1; /* do we own the lock? */
44
int sigblock : 1; /* block signals when locked */
52
* @datafile: the file that should be covered by the lock
53
* @id: unique linkfile identifier or 0 (default is getpid())
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)
59
struct libmnt_lock *ml = NULL;
60
char *lo = NULL, *ln = NULL;
66
if (asprintf(&lo, "%s~", datafile) == -1) {
70
if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
74
ml = calloc(1, sizeof(*ml) );
82
DBG(LOCKS, mnt_debug_h(ml, "alloc: linkfile=%s, lockfile=%s", ln, lo));
94
* @ml: struct libmnt_lock handler
96
* Deallocates mnt_lock.
98
void mnt_free_lock(struct libmnt_lock *ml)
102
DBG(LOCKS, mnt_debug_h(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
109
* mnt_lock_block_signals:
110
* @ml: struct libmnt_lock handler
111
* @enable: TRUE/FALSE
113
* Block/unblock signals when the lock is locked, the signals are not blocked
116
* Returns: <0 on error, 0 on success.
118
int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
122
DBG(LOCKS, mnt_debug_h(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
123
ml->sigblock = enable ? 1 : 0;
128
* Returns path to lockfile.
130
static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
132
return ml ? ml->lockfile : NULL;
136
* Note that the filename is generated by mnt_new_lock() and depends on
137
* getpid() or 'id' argument of the mnt_new_lock() function.
139
* Returns: unique (per process/thread) path to linkfile.
141
static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
143
return ml ? ml->linkfile : NULL;
146
static void mnt_lockalrm_handler(int sig)
148
/* do nothing, say nothing, be nothing */
152
* Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
153
* fcntl() to avoid never ending waiting.
155
* Returns: 0 on success, 1 on timeout, -errno on error.
157
static int mnt_wait_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
160
struct sigaction sa, osa;
163
gettimeofday(&now, NULL);
165
if (now.tv_sec >= maxtime)
166
return 1; /* timeout */
168
/* setup ALARM handler -- we don't want to wait forever */
170
sa.sa_handler = mnt_lockalrm_handler;
171
sigfillset (&sa.sa_mask);
173
sigaction(SIGALRM, &sa, &osa);
175
DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid()));
177
alarm(maxtime - now.tv_sec);
178
if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
179
ret = errno == EINTR ? 1 : -errno;
182
/* restore old sigaction */
183
sigaction(SIGALRM, &osa, NULL);
185
DBG(LOCKS, mnt_debug_h(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
191
* Create the lock file.
193
* The old code here used flock on a lock file /etc/mtab~ and deleted
194
* this lock file afterwards. However, as rgooch remarks, that has a
195
* race: a second mount may be waiting on the lock and proceed as
196
* soon as the lock file is deleted by the first mount, and immediately
197
* afterwards a third mount comes, creates a new /etc/mtab~, applies
198
* flock to that, and also proceeds, so that the second and third mount
199
* now both are scribbling in /etc/mtab.
201
* The new code uses a link() instead of a creat(), where we proceed
202
* only if it was us that created the lock, and hence we always have
203
* to delete the lock afterwards. Now the use of flock() is in principle
204
* superfluous, but avoids an arbitrary sleep().
206
* Where does the link point to? Obvious choices are mtab and mtab~~.
207
* HJLu points out that the latter leads to races. Right now we use
208
* mtab~.<pid> instead.
211
* The original mount locking code has used sleep(1) between attempts and
212
* maximal number of attempts has been 5.
214
* There was very small number of attempts and extremely long waiting (1s)
215
* that is useless on machines with large number of mount processes.
217
* Now we wait few thousand microseconds between attempts and we have a global
218
* time limit (30s) rather than limit for number of attempts. The advantage
219
* is that this method also counts time which we spend in fcntl(F_SETLKW) and
220
* number of attempts is not restricted.
221
* -- kzak@redhat.com [Mar-2007]
224
* This mtab locking code has been refactored and moved to libmount. The mtab
225
* locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and
226
* backwardly compatible code.
228
* Don't forget that this code has to be compatible with 3rd party mounts
229
* (/sbin/mount.<foo>) and has to work with NFS.
230
* -- kzak@redhat.com [May-2009]
233
/* maximum seconds between first and last attempt */
234
#define MOUNTLOCK_MAXTIME 30
236
/* sleep time (in microseconds, max=999999) between attempts */
237
#define MOUNTLOCK_WAITTIME 5000
243
* Unlocks the file. The function could be called independently on the
244
* lock status (for example from exit(3)).
246
void mnt_unlock_file(struct libmnt_lock *ml)
251
if (!ml->locked && ml->lockfile && ml->linkfile)
253
/* We have (probably) all files, but we don't own the lock,
254
* Really? Check it! Maybe ml->locked wasn't set properly
255
* because code was interrupted by signal. Paranoia? Yes.
257
* We own the lock when linkfile == lockfile.
261
if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
262
lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
266
DBG(LOCKS, mnt_debug_h(ml, "(%d) %s", getpid(),
267
ml->locked ? "unlocking" : "cleaning"));
270
unlink(ml->linkfile);
271
if (ml->lockfile_fd >= 0)
272
close(ml->lockfile_fd);
273
if (ml->locked && ml->lockfile) {
274
unlink(ml->lockfile);
275
DBG(LOCKS, mnt_debug_h(ml, "unlink %s", ml->lockfile));
278
ml->lockfile_fd = -1;
281
DBG(LOCKS, mnt_debug_h(ml, "restoring sigmask"));
282
sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
289
* @ml: pointer to struct libmnt_lock instance
291
* Creates lock file (e.g. /etc/mtab~). Note that this function uses
294
* Your application has to always call mnt_unlock_file() before exit.
298
* 1. create linkfile (e.g. /etc/mtab~.$PID)
299
* 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
301
* 3. a) link() success: setups F_SETLK lock (see fcnlt(2))
302
* b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2.
308
* struct libmnt_lock *ml;
310
* void unlock_fallback(void)
314
* mnt_unlock_file(ml);
323
* if (!(mtab = mnt_get_mtab_path()))
324
* return 0; // system without mtab
325
* if (!(ml = mnt_new_lock(mtab, 0)))
326
* return -1; // error
328
* atexit(unlock_fallback);
330
* if (mnt_lock_file(ml) != 0) {
331
* printf(stderr, "cannot create %s lockfile\n",
332
* mnt_lock_get_lockfile(ml));
336
* ... modify mtab ...
338
* mnt_unlock_file(ml);
346
* Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
347
* of stale lock file).
349
int mnt_lock_file(struct libmnt_lock *ml)
352
struct timespec waittime;
353
struct timeval maxtime;
354
const char *lockfile, *linkfile;
361
lockfile = mnt_lock_get_lockfile(ml);
364
linkfile = mnt_lock_get_linkfile(ml);
370
* Block all signals when locked, mnt_unlock_file() will
371
* restore the old mask.
375
sigemptyset(&ml->oldsigmask);
377
sigdelset(&sigs, SIGTRAP);
378
sigdelset(&sigs, SIGALRM);
379
sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
382
i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
384
/* linkfile does not exist (as a file) and we cannot create it.
385
* Read-only or full filesystem? Too many files open in the system?
393
gettimeofday(&maxtime, NULL);
394
maxtime.tv_sec += MOUNTLOCK_MAXTIME;
397
waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
399
/* Repeat until it was us who made the link */
400
while (!ml->locked) {
405
j = link(linkfile, lockfile);
409
if (j < 0 && errno != EEXIST) {
414
ml->lockfile_fd = open(lockfile, O_WRONLY);
416
if (ml->lockfile_fd < 0) {
417
/* Strange... Maybe the file was just deleted? */
419
gettimeofday(&now, NULL);
420
if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
429
flock.l_type = F_WRLCK;
430
flock.l_whence = SEEK_SET;
435
/* We made the link. Now claim the lock. */
436
if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) {
437
DBG(LOCKS, mnt_debug_h(ml,
438
"%s: can't F_SETLK lockfile, errno=%d\n",
440
/* proceed, since it was us who created the lockfile anyway */
444
/* Someone else made the link. Wait. */
445
int err = mnt_wait_lock(ml, &flock, maxtime.tv_sec);
448
DBG(LOCKS, mnt_debug_h(ml,
449
"%s: can't create link: time out (perhaps "
450
"there is a stale lock file?)", lockfile));
454
} else if (err < 0) {
458
nanosleep(&waittime, NULL);
459
close(ml->lockfile_fd);
460
ml->lockfile_fd = -1;
463
DBG(LOCKS, mnt_debug_h(ml, "%s: (%d) successfully locked",
464
lockfile, getpid()));
476
struct libmnt_lock *lock;
479
* read number from @filename, increment the number and
480
* write the number back to the file
482
void increment_data(const char *filename, int verbose, int loopno)
488
if (!(f = fopen(filename, "r")))
489
err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
491
if (!fgets(buf, sizeof(buf), f))
492
err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
497
if (!(f = fopen(filename, "w")))
498
err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
500
fprintf(f, "%ld", num);
504
fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
505
filename, num - 1, num, loopno);
508
void clean_lock(void)
512
mnt_unlock_file(lock);
516
void sig_handler(int sig)
518
errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
521
int test_lock(struct libmnt_test *ts, int argc, char *argv[])
526
const char *datafile = NULL;
527
int verbose = 0, loops = 0, l, idx = 1;
532
if (strcmp(argv[idx], "--synctime") == 0) {
533
synctime = (time_t) atol(argv[idx + 1]);
536
if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
542
datafile = argv[idx++];
544
loops = atoi(argv[idx++]);
546
if (!datafile || !loops)
550
fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
551
getpid(), (int) synctime, datafile, loops);
555
/* be paranoid and call exit() (=clean_lock()) for all signals */
560
sa.sa_handler = sig_handler;
562
sigfillset(&sa.sa_mask);
564
while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
565
sigaction (sig, &sa, (struct sigaction *) 0);
568
/* start the test in exactly defined time */
570
gettimeofday(&tv, NULL);
571
if (synctime && synctime - tv.tv_sec > 1) {
572
usecs = ((synctime - tv.tv_sec) * 1000000UL) -
573
(1000000UL - tv.tv_usec);
578
for (l = 0; l < loops; l++) {
579
lock = mnt_new_lock(datafile, 0);
583
if (mnt_lock_file(lock) != 0) {
584
fprintf(stderr, "%d: failed to lock %s file\n",
589
increment_data(datafile, verbose, l);
591
mnt_unlock_file(lock);
595
/* The mount command usually finish after mtab update. We
596
* simulate this via short sleep -- it's also enough to make
597
* concurrent processes happy.
607
* Note that this test should be executed from a script that creates many
608
* parallel processes, otherwise this test does not make sense.
610
int main(int argc, char *argv[])
612
struct libmnt_test tss[] = {
613
{ "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
614
"increment a number in datafile" },
618
return mnt_run_test(tss, argc, argv);
621
#endif /* TEST_PROGRAM */