]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/lock.c
Merge branch 'master', remote-tracking branch 'upstream/master' into sfdisk
[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 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.
16 */
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <sys/file.h>
30
31 #include "pathnames.h"
32 #include "nls.h"
33 #include "c.h"
34
35 #include "mountP.h"
36
37 /*
38 * lock handler
39 */
40 struct libmnt_lock {
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 */
44
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 */
48
49 sigset_t oldsigmask;
50 };
51
52
53 /**
54 * mnt_new_lock:
55 * @datafile: the file that should be covered by the lock
56 * @id: unique linkfile identifier or 0 (default is getpid())
57 *
58 * Returns: newly allocated lock handler or NULL on case of error.
59 */
60 struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
61 {
62 struct libmnt_lock *ml = NULL;
63 char *lo = NULL, *ln = NULL;
64 size_t losz;
65
66 if (!datafile)
67 return NULL;
68
69 /* for flock we use "foo.lock, for mtab "foo~"
70 */
71 losz = strlen(datafile) + sizeof(".lock");
72 lo = malloc(losz);
73 if (!lo)
74 goto err;
75
76 /* default is mtab~ lock */
77 snprintf(lo, losz, "%s~", datafile);
78
79 if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
80 ln = NULL;
81 goto err;
82 }
83 ml = calloc(1, sizeof(*ml) );
84 if (!ml)
85 goto err;
86
87 ml->lockfile_fd = -1;
88 ml->linkfile = ln;
89 ml->lockfile = lo;
90
91 DBG(LOCKS, mnt_debug_h(ml, "alloc: default linkfile=%s, lockfile=%s", ln, lo));
92 return ml;
93 err:
94 free(lo);
95 free(ln);
96 free(ml);
97 return NULL;
98 }
99
100
101 /**
102 * mnt_free_lock:
103 * @ml: struct libmnt_lock handler
104 *
105 * Deallocates mnt_lock.
106 */
107 void mnt_free_lock(struct libmnt_lock *ml)
108 {
109 if (!ml)
110 return;
111 DBG(LOCKS, mnt_debug_h(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
112 free(ml->lockfile);
113 free(ml->linkfile);
114 free(ml);
115 }
116
117 /**
118 * mnt_lock_block_signals:
119 * @ml: struct libmnt_lock handler
120 * @enable: TRUE/FALSE
121 *
122 * Block/unblock signals when the lock is locked, the signals are not blocked
123 * by default.
124 *
125 * Returns: <0 on error, 0 on success.
126 */
127 int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
128 {
129 if (!ml)
130 return -EINVAL;
131 DBG(LOCKS, mnt_debug_h(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
132 ml->sigblock = enable ? 1 : 0;
133 return 0;
134 }
135
136 /* don't export this to API
137 */
138 int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable)
139 {
140 size_t sz;
141
142 if (!ml)
143 return -EINVAL;
144
145 assert(ml->lockfile);
146
147 DBG(LOCKS, mnt_debug_h(ml, "flock: %s", enable ? "ENABLED" : "DISABLED"));
148 ml->simplelock = enable ? 1 : 0;
149
150 sz = strlen(ml->lockfile);
151
152 /* Change lock name:
153 *
154 * flock: "<name>.lock"
155 * mtab lock: "<name>~"
156 */
157 if (ml->simplelock && endswith(ml->lockfile, "~"))
158 memcpy(ml->lockfile + sz - 1, ".lock", 6);
159
160 else if (!ml->simplelock && endswith(ml->lockfile, ".lock"))
161 memcpy(ml->lockfile + sz - 5, "~", 2);
162
163 DBG(LOCKS, mnt_debug_h(ml, "new lock filename: '%s'", ml->lockfile));
164 return 0;
165 }
166
167 /*
168 * Returns path to lockfile.
169 */
170 static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
171 {
172 return ml ? ml->lockfile : NULL;
173 }
174
175 /*
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.
178 *
179 * Returns: unique (per process/thread) path to linkfile.
180 */
181 static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
182 {
183 return ml ? ml->linkfile : NULL;
184 }
185
186 /*
187 * Simple flocking
188 */
189 static void unlock_simplelock(struct libmnt_lock *ml)
190 {
191 assert(ml);
192 assert(ml->simplelock);
193
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);
198 }
199 }
200
201 static int lock_simplelock(struct libmnt_lock *ml)
202 {
203 const char *lfile;
204 int rc;
205
206 assert(ml);
207 assert(ml->simplelock);
208
209 lfile = mnt_lock_get_lockfile(ml);
210
211 DBG(LOCKS, mnt_debug_h(ml, "%s: locking", lfile));
212
213 if (ml->sigblock) {
214 sigset_t sigs;
215 sigemptyset(&ml->oldsigmask);
216 sigfillset(&sigs);
217 sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
218 }
219
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) {
223 rc = -errno;
224 goto err;
225 }
226
227 while (flock(ml->lockfile_fd, LOCK_EX) < 0) {
228 int errsv;
229 if ((errno == EAGAIN) || (errno == EINTR))
230 continue;
231 errsv = errno;
232 close(ml->lockfile_fd);
233 ml->lockfile_fd = -1;
234 rc = -errsv;
235 goto err;
236 }
237 ml->locked = 1;
238 return 0;
239 err:
240 if (ml->sigblock)
241 sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
242 return rc;
243 }
244
245 /*
246 * traditional mtab locking
247 */
248
249 static void mnt_lockalrm_handler(int sig)
250 {
251 /* do nothing, say nothing, be nothing */
252 }
253
254 /*
255 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
256 * fcntl() to avoid never ending waiting.
257 *
258 * Returns: 0 on success, 1 on timeout, -errno on error.
259 */
260 static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
261 {
262 struct timeval now;
263 struct sigaction sa, osa;
264 int ret = 0;
265
266 gettimeofday(&now, NULL);
267
268 if (now.tv_sec >= maxtime)
269 return 1; /* timeout */
270
271 /* setup ALARM handler -- we don't want to wait forever */
272 sa.sa_flags = 0;
273 sa.sa_handler = mnt_lockalrm_handler;
274 sigfillset (&sa.sa_mask);
275
276 sigaction(SIGALRM, &sa, &osa);
277
278 DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid()));
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, mnt_debug_h(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 * now both are 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 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 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]
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 first and 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 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.
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, mnt_debug_h(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, 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 gettimeofday(&maxtime, NULL);
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);
438
439 if (ml->lockfile_fd < 0) {
440 /* Strange... Maybe the file was just deleted? */
441 int errsv = errno;
442 gettimeofday(&now, NULL);
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, mnt_debug_h(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, mnt_debug_h(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, mnt_debug_h(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 lock file (e.g. /etc/mtab~). Note that this function may
502 * use alarm().
503 *
504 * Your application has to always 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 on 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, mnt_debug_h(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, mnt_debug_h(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")))
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")))
580 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
581
582 fprintf(f, "%ld", num);
583 fclose(f);
584
585 if (verbose)
586 fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
587 filename, num - 1, num, loopno);
588 }
589
590 void clean_lock(void)
591 {
592 if (!lock)
593 return;
594 mnt_unlock_file(lock);
595 mnt_free_lock(lock);
596 }
597
598 void sig_handler(int sig)
599 {
600 errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
601 }
602
603 int test_lock(struct libmnt_test *ts, int argc, char *argv[])
604 {
605 time_t synctime = 0;
606 unsigned int usecs;
607 struct timeval tv;
608 const char *datafile = NULL;
609 int verbose = 0, loops = 0, l, idx = 1;
610
611 if (argc < 3)
612 return -EINVAL;
613
614 if (strcmp(argv[idx], "--synctime") == 0) {
615 synctime = (time_t) atol(argv[idx + 1]);
616 idx += 2;
617 }
618 if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
619 verbose = 1;
620 idx++;
621 }
622
623 if (idx < argc)
624 datafile = argv[idx++];
625 if (idx < argc)
626 loops = atoi(argv[idx++]);
627
628 if (!datafile || !loops)
629 return -EINVAL;
630
631 if (verbose)
632 fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
633 getpid(), (int) synctime, datafile, loops);
634
635 atexit(clean_lock);
636
637 /* be paranoid and call exit() (=clean_lock()) for all signals */
638 {
639 int sig = 0;
640 struct sigaction sa;
641
642 sa.sa_handler = sig_handler;
643 sa.sa_flags = 0;
644 sigfillset(&sa.sa_mask);
645
646 while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
647 sigaction (sig, &sa, (struct sigaction *) 0);
648 }
649
650 /* start the test in exactly defined time */
651 if (synctime) {
652 gettimeofday(&tv, NULL);
653 if (synctime && synctime - tv.tv_sec > 1) {
654 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
655 (1000000UL - tv.tv_usec);
656 usleep(usecs);
657 }
658 }
659
660 for (l = 0; l < loops; l++) {
661 lock = mnt_new_lock(datafile, 0);
662 if (!lock)
663 return -1;
664
665 if (mnt_lock_file(lock) != 0) {
666 fprintf(stderr, "%d: failed to lock %s file\n",
667 getpid(), datafile);
668 return -1;
669 }
670
671 increment_data(datafile, verbose, l);
672
673 mnt_unlock_file(lock);
674 mnt_free_lock(lock);
675 lock = NULL;
676
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.
680 */
681 if (synctime)
682 usleep(25000);
683 }
684
685 return 0;
686 }
687
688 /*
689 * Note that this test should be executed from a script that creates many
690 * parallel processes, otherwise this test does not make sense.
691 */
692 int main(int argc, char *argv[])
693 {
694 struct libmnt_test tss[] = {
695 { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
696 "increment a number in datafile" },
697 { NULL }
698 };
699
700 return mnt_run_test(tss, argc, argv);
701 }
702
703 #endif /* TEST_PROGRAM */