]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/lock.c
libmount: add options map to docs
[thirdparty/util-linux.git] / libmount / src / lock.c
1 /*
2 * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
8 /**
9 * SECTION: lock
10 * @title: Locking
11 * @short_description: locking methods for /etc/mtab or another libmount files
12 *
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.
16 */
17 #include <sys/time.h>
18 #include <time.h>
19 #include <signal.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <sys/file.h>
23
24 #include "strutils.h"
25 #include "closestream.h"
26 #include "pathnames.h"
27 #include "mountP.h"
28 #include "monotonic.h"
29
30 /*
31 * lock handler
32 */
33 struct libmnt_lock {
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 */
37
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 */
41
42 sigset_t oldsigmask;
43 };
44
45
46 /**
47 * mnt_new_lock:
48 * @datafile: the file that should be covered by the lock
49 * @id: unique linkfile identifier or 0 (default is getpid())
50 *
51 * Returns: newly allocated lock handler or NULL on case of error.
52 */
53 struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
54 {
55 struct libmnt_lock *ml = NULL;
56 char *lo = NULL, *ln = NULL;
57 size_t losz;
58
59 assert(datafile);
60
61 /* for flock we use "foo.lock, for mtab "foo~"
62 */
63 losz = strlen(datafile) + sizeof(".lock");
64 lo = malloc(losz);
65 if (!lo)
66 goto err;
67
68 /* default is mtab~ lock */
69 snprintf(lo, losz, "%s~", datafile);
70
71 if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
72 ln = NULL;
73 goto err;
74 }
75 ml = calloc(1, sizeof(*ml) );
76 if (!ml)
77 goto err;
78
79 ml->lockfile_fd = -1;
80 ml->linkfile = ln;
81 ml->lockfile = lo;
82
83 DBG(LOCKS, ul_debugobj(ml, "alloc: default linkfile=%s, lockfile=%s", ln, lo));
84 return ml;
85 err:
86 free(lo);
87 free(ln);
88 free(ml);
89 return NULL;
90 }
91
92
93 /**
94 * mnt_free_lock:
95 * @ml: struct libmnt_lock handler
96 *
97 * Deallocates mnt_lock.
98 */
99 void mnt_free_lock(struct libmnt_lock *ml)
100 {
101 if (!ml)
102 return;
103 DBG(LOCKS, ul_debugobj(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
104 free(ml->lockfile);
105 free(ml->linkfile);
106 free(ml);
107 }
108
109 /**
110 * mnt_lock_block_signals:
111 * @ml: struct libmnt_lock handler
112 * @enable: TRUE/FALSE
113 *
114 * Block/unblock signals when the lock is locked, the signals are not blocked
115 * by default.
116 *
117 * Returns: <0 on error, 0 on success.
118 */
119 int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
120 {
121 if (!ml)
122 return -EINVAL;
123 DBG(LOCKS, ul_debugobj(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
124 ml->sigblock = enable ? 1 : 0;
125 return 0;
126 }
127
128 /* don't export this to API
129 */
130 int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable)
131 {
132 size_t sz;
133
134 if (!ml)
135 return -EINVAL;
136
137 assert(ml->lockfile);
138
139 DBG(LOCKS, ul_debugobj(ml, "flock: %s", enable ? "ENABLED" : "DISABLED"));
140 ml->simplelock = enable ? 1 : 0;
141
142 sz = strlen(ml->lockfile);
143 assert(sz);
144
145 if (sz < 1)
146 return -EINVAL;
147
148 /* Change lock name:
149 *
150 * flock: "<name>.lock"
151 * mtab lock: "<name>~"
152 */
153 if (ml->simplelock && endswith(ml->lockfile, "~"))
154 memcpy(ml->lockfile + sz - 1, ".lock", 6);
155
156 else if (!ml->simplelock && endswith(ml->lockfile, ".lock"))
157 memcpy(ml->lockfile + sz - 5, "~", 2);
158
159 DBG(LOCKS, ul_debugobj(ml, "new lock filename: '%s'", ml->lockfile));
160 return 0;
161 }
162
163 /*
164 * Returns path to lockfile.
165 */
166 static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
167 {
168 return ml ? ml->lockfile : NULL;
169 }
170
171 /*
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.
174 *
175 * Returns: unique (per process/thread) path to linkfile.
176 */
177 static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
178 {
179 return ml ? ml->linkfile : NULL;
180 }
181
182 /*
183 * Simple flocking
184 */
185 static void unlock_simplelock(struct libmnt_lock *ml)
186 {
187 assert(ml);
188 assert(ml->simplelock);
189
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);
194 }
195 }
196
197 static int lock_simplelock(struct libmnt_lock *ml)
198 {
199 const char *lfile;
200 int rc;
201
202 assert(ml);
203 assert(ml->simplelock);
204
205 lfile = mnt_lock_get_lockfile(ml);
206
207 DBG(LOCKS, ul_debugobj(ml, "%s: locking", lfile));
208
209 if (ml->sigblock) {
210 sigset_t sigs;
211 sigemptyset(&ml->oldsigmask);
212 sigfillset(&sigs);
213 sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
214 }
215
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) {
219 rc = -errno;
220 goto err;
221 }
222
223 while (flock(ml->lockfile_fd, LOCK_EX) < 0) {
224 int errsv;
225 if ((errno == EAGAIN) || (errno == EINTR))
226 continue;
227 errsv = errno;
228 close(ml->lockfile_fd);
229 ml->lockfile_fd = -1;
230 rc = -errsv;
231 goto err;
232 }
233 ml->locked = 1;
234 return 0;
235 err:
236 if (ml->sigblock)
237 sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
238 return rc;
239 }
240
241 /*
242 * traditional mtab locking
243 */
244
245 static void mnt_lockalrm_handler(int sig __attribute__((__unused__)))
246 {
247 /* do nothing, say nothing, be nothing */
248 }
249
250 /*
251 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
252 * fcntl() to avoid neverending waiting.
253 *
254 * Returns: 0 on success, 1 on timeout, -errno on error.
255 */
256 static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
257 {
258 struct timeval now;
259 struct sigaction sa, osa;
260 int ret = 0;
261
262 gettime_monotonic(&now);
263 DBG(LOCKS, ul_debugobj(ml, "(%d) waiting for F_SETLKW (now=%lu, maxtime=%lu, diff=%lu)",
264 getpid(),
265 (unsigned long) now.tv_sec,
266 (unsigned long) maxtime,
267 (unsigned long) (maxtime - now.tv_sec)));
268
269 if (now.tv_sec >= maxtime)
270 return 1; /* timeout */
271
272 /* setup ALARM handler -- we don't want to wait forever */
273 sa.sa_flags = 0;
274 sa.sa_handler = mnt_lockalrm_handler;
275 sigfillset (&sa.sa_mask);
276
277 sigaction(SIGALRM, &sa, &osa);
278
279
280 alarm(maxtime - now.tv_sec);
281 if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
282 ret = errno == EINTR ? 1 : -errno;
283 alarm(0);
284
285 /* restore old sigaction */
286 sigaction(SIGALRM, &osa, NULL);
287
288 DBG(LOCKS, ul_debugobj(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
289 getpid(), ret));
290 return ret;
291 }
292
293 /*
294 * Create the mtab lock file.
295 *
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.
303 *
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().
308 *
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.
312 *
313 *
314 * The original mount locking code has used sleep(1) between attempts and
315 * maximal number of attempts has been 5.
316 *
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.
319 *
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]
325 *
326 *
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.
330 *
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]
334 */
335
336 /* maximum seconds between the first and the last attempt */
337 #define MOUNTLOCK_MAXTIME 30
338
339 /* sleep time (in microseconds, max=999999) between attempts */
340 #define MOUNTLOCK_WAITTIME 5000
341
342 static void unlock_mtab(struct libmnt_lock *ml)
343 {
344 if (!ml)
345 return;
346
347 if (!ml->locked && ml->lockfile && ml->linkfile)
348 {
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.
352 *
353 * We own the lock when linkfile == lockfile.
354 */
355 struct stat lo, li;
356
357 if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
358 lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
359 ml->locked = 1;
360 }
361
362 if (ml->linkfile)
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));
369 }
370 }
371
372 static int lock_mtab(struct libmnt_lock *ml)
373 {
374 int i, rc = -1;
375 struct timespec waittime;
376 struct timeval maxtime;
377 const char *lockfile, *linkfile;
378
379 if (!ml)
380 return -EINVAL;
381 if (ml->locked)
382 return 0;
383
384 lockfile = mnt_lock_get_lockfile(ml);
385 if (!lockfile)
386 return -EINVAL;
387 linkfile = mnt_lock_get_linkfile(ml);
388 if (!linkfile)
389 return -EINVAL;
390
391 if (ml->sigblock) {
392 /*
393 * Block all signals when locked, mnt_unlock_file() will
394 * restore the old mask.
395 */
396 sigset_t sigs;
397
398 sigemptyset(&ml->oldsigmask);
399 sigfillset(&sigs);
400 sigdelset(&sigs, SIGTRAP);
401 sigdelset(&sigs, SIGALRM);
402 sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
403 }
404
405 i = open(linkfile, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
406 if (i < 0) {
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?
409 */
410 if (errno > 0)
411 rc = -errno;
412 goto failed;
413 }
414 close(i);
415
416 gettime_monotonic(&maxtime);
417 maxtime.tv_sec += MOUNTLOCK_MAXTIME;
418
419 waittime.tv_sec = 0;
420 waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
421
422 /* Repeat until it was us who made the link */
423 while (!ml->locked) {
424 struct timeval now;
425 struct flock flock;
426 int j;
427
428 j = link(linkfile, lockfile);
429 if (j == 0)
430 ml->locked = 1;
431
432 if (j < 0 && errno != EEXIST) {
433 if (errno > 0)
434 rc = -errno;
435 goto failed;
436 }
437 ml->lockfile_fd = open(lockfile, O_WRONLY|O_CLOEXEC);
438
439 if (ml->lockfile_fd < 0) {
440 /* Strange... Maybe the file was just deleted? */
441 int errsv = errno;
442 gettime_monotonic(&now);
443 if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
444 ml->locked = 0;
445 continue;
446 }
447 if (errsv > 0)
448 rc = -errsv;
449 goto failed;
450 }
451
452 flock.l_type = F_WRLCK;
453 flock.l_whence = SEEK_SET;
454 flock.l_start = 0;
455 flock.l_len = 0;
456
457 if (ml->locked) {
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",
462 lockfile, errno));
463 /* proceed, since it was us who created the lockfile anyway */
464 }
465 break;
466 } else {
467 /* Someone else made the link. Wait. */
468 int err = mnt_wait_mtab_lock(ml, &flock, maxtime.tv_sec);
469
470 if (err == 1) {
471 DBG(LOCKS, ul_debugobj(ml,
472 "%s: can't create link: time out (perhaps "
473 "there is a stale lock file?)", lockfile));
474 rc = -ETIMEDOUT;
475 goto failed;
476
477 } else if (err < 0) {
478 rc = err;
479 goto failed;
480 }
481 nanosleep(&waittime, NULL);
482 close(ml->lockfile_fd);
483 ml->lockfile_fd = -1;
484 }
485 }
486 DBG(LOCKS, ul_debugobj(ml, "%s: (%d) successfully locked",
487 lockfile, getpid()));
488 unlink(linkfile);
489 return 0;
490
491 failed:
492 mnt_unlock_file(ml);
493 return rc;
494 }
495
496
497 /**
498 * mnt_lock_file
499 * @ml: pointer to struct libmnt_lock instance
500 *
501 * Creates a lock file (e.g. /etc/mtab~). Note that this function may
502 * use alarm().
503 *
504 * Your application always has to call mnt_unlock_file() before exit.
505 *
506 * Traditional mtab locking scheme:
507 *
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.
512 *
513 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
514 * of stale lock file).
515 */
516 int mnt_lock_file(struct libmnt_lock *ml)
517 {
518 if (!ml)
519 return -EINVAL;
520
521 if (ml->simplelock)
522 return lock_simplelock(ml);
523
524 return lock_mtab(ml);
525 }
526
527 /**
528 * mnt_unlock_file:
529 * @ml: lock struct
530 *
531 * Unlocks the file. The function could be called independently of the
532 * lock status (for example from exit(3)).
533 */
534 void mnt_unlock_file(struct libmnt_lock *ml)
535 {
536 if (!ml)
537 return;
538
539 DBG(LOCKS, ul_debugobj(ml, "(%d) %s", getpid(),
540 ml->locked ? "unlocking" : "cleaning"));
541
542 if (ml->simplelock)
543 unlock_simplelock(ml);
544 else
545 unlock_mtab(ml);
546
547 ml->locked = 0;
548 ml->lockfile_fd = -1;
549
550 if (ml->sigblock) {
551 DBG(LOCKS, ul_debugobj(ml, "restoring sigmask"));
552 sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
553 }
554 }
555
556 #ifdef TEST_PROGRAM
557
558 struct libmnt_lock *lock;
559
560 /*
561 * read number from @filename, increment the number and
562 * write the number back to the file
563 */
564 void increment_data(const char *filename, int verbose, int loopno)
565 {
566 long num;
567 FILE *f;
568 char buf[256];
569
570 if (!(f = fopen(filename, "r" UL_CLOEXECSTR)))
571 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
572
573 if (!fgets(buf, sizeof(buf), f))
574 err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
575
576 fclose(f);
577 num = atol(buf) + 1;
578
579 if (!(f = fopen(filename, "w" UL_CLOEXECSTR)))
580 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
581
582 fprintf(f, "%ld", num);
583
584 if (close_stream(f) != 0)
585 err(EXIT_FAILURE, "write failed: %s", filename);
586
587 if (verbose)
588 fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
589 filename, num - 1, num, loopno);
590 }
591
592 void clean_lock(void)
593 {
594 if (!lock)
595 return;
596 mnt_unlock_file(lock);
597 mnt_free_lock(lock);
598 }
599
600 void __attribute__((__noreturn__)) sig_handler(int sig)
601 {
602 errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
603 }
604
605 int test_lock(struct libmnt_test *ts, int argc, char *argv[])
606 {
607 time_t synctime = 0;
608 unsigned int usecs;
609 const char *datafile = NULL;
610 int verbose = 0, loops = 0, l, idx = 1;
611
612 if (argc < 3)
613 return -EINVAL;
614
615 if (strcmp(argv[idx], "--synctime") == 0) {
616 synctime = (time_t) atol(argv[idx + 1]);
617 idx += 2;
618 }
619 if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
620 verbose = 1;
621 idx++;
622 }
623
624 if (idx < argc)
625 datafile = argv[idx++];
626 if (idx < argc)
627 loops = atoi(argv[idx++]);
628
629 if (!datafile || !loops)
630 return -EINVAL;
631
632 if (verbose)
633 fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
634 getpid(), (int) synctime, datafile, loops);
635
636 atexit(clean_lock);
637
638 /* be paranoid and call exit() (=clean_lock()) for all signals */
639 {
640 int sig = 0;
641 struct sigaction sa;
642
643 sa.sa_handler = sig_handler;
644 sa.sa_flags = 0;
645 sigfillset(&sa.sa_mask);
646
647 while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
648 sigaction (sig, &sa, (struct sigaction *) 0);
649 }
650
651 /* start the test in exactly defined time */
652 if (synctime) {
653 struct timeval tv;
654
655 gettimeofday(&tv, NULL);
656 if (synctime && synctime - tv.tv_sec > 1) {
657 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
658 (1000000UL - tv.tv_usec);
659 xusleep(usecs);
660 }
661 }
662
663 for (l = 0; l < loops; l++) {
664 lock = mnt_new_lock(datafile, 0);
665 if (!lock)
666 return -1;
667
668 if (mnt_lock_file(lock) != 0) {
669 fprintf(stderr, "%d: failed to lock %s file\n",
670 getpid(), datafile);
671 return -1;
672 }
673
674 increment_data(datafile, verbose, l);
675
676 mnt_unlock_file(lock);
677 mnt_free_lock(lock);
678 lock = NULL;
679
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.
683 */
684 if (synctime)
685 xusleep(25000);
686 }
687
688 return 0;
689 }
690
691 /*
692 * Note that this test should be executed from a script that creates many
693 * parallel processes, otherwise this test does not make sense.
694 */
695 int main(int argc, char *argv[])
696 {
697 struct libmnt_test tss[] = {
698 { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
699 "increment a number in datafile" },
700 { NULL }
701 };
702
703 return mnt_run_test(tss, argc, argv);
704 }
705
706 #endif /* TEST_PROGRAM */