]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/lock.c
build-sys: provide alternatives for err, errx, warn and warnx
[thirdparty/util-linux.git] / shlibs / mount / src / lock.c
CommitLineData
27c6d415
KZ
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 */
192c6aad
KZ
7
8/**
9 * SECTION: lock
10 * @title: Mtab locking
11 * @short_description: locking methods for work with /etc/mtab
12 *
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
15 * access the file.
16 */
27c6d415
KZ
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
30#include "pathnames.h"
31#include "nls.h"
eb76ca98 32#include "c.h"
27c6d415
KZ
33
34#include "mountP.h"
35
36/*
37 * lock handler
38 */
68164f6c 39struct libmnt_lock {
27c6d415
KZ
40 char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
41 char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */
42 int lockfile_fd; /* lock file descriptor */
43 int locked; /* do we own the lock? */
44};
45
46
47/**
48 * mnt_new_lock:
0f32f1e2 49 * @datafile: the file that should be covered by the lock
27c6d415
KZ
50 * @id: unique linkfile identifier or 0 (default is getpid())
51 *
192c6aad 52 * Returns: newly allocated lock handler or NULL on case of error.
27c6d415 53 */
68164f6c 54struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
27c6d415 55{
68164f6c 56 struct libmnt_lock *ml = NULL;
66c33aab 57 char *lo = NULL, *ln = NULL;
27c6d415 58
66c33aab
KZ
59 /* lockfile */
60 if (!datafile)
27c6d415
KZ
61 return NULL;
62
66c33aab
KZ
63 if (asprintf(&lo, "%s~", datafile) == -1) {
64 lo = NULL;
65 goto err;
66 }
67 if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
68 ln = NULL;
69 goto err;
27c6d415 70 }
68164f6c 71 ml = calloc(1, sizeof(*ml) );
66c33aab
KZ
72 if (!ml)
73 goto err;
74
75 ml->lockfile_fd = -1;
76 ml->linkfile = ln;
77 ml->lockfile = lo;
3f31a959 78
9e10ad3d 79 DBG(LOCKS, mnt_debug_h(ml, "alloc: linkfile=%s, lockfile=%s", ln, lo));
27c6d415 80 return ml;
66c33aab
KZ
81err:
82 free(lo);
83 free(ln);
84 free(ml);
85 return NULL;
27c6d415
KZ
86}
87
66c33aab 88
27c6d415
KZ
89/**
90 * mnt_free_lock:
68164f6c 91 * @ml: struct libmnt_lock handler
27c6d415
KZ
92 *
93 * Deallocates mnt_lock.
94 */
68164f6c 95void mnt_free_lock(struct libmnt_lock *ml)
27c6d415
KZ
96{
97 if (!ml)
98 return;
3f31a959 99 DBG(LOCKS, mnt_debug_h(ml, "free"));
27c6d415
KZ
100 free(ml->lockfile);
101 free(ml->linkfile);
102 free(ml);
103}
104
0f32f1e2
KZ
105/*
106 * Returns path to lockfile.
27c6d415 107 */
68164f6c 108static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
27c6d415 109{
66c33aab 110 return ml ? ml->lockfile : NULL;
27c6d415
KZ
111}
112
0f32f1e2 113/*
66c33aab
KZ
114 * Note that the filename is generated by mnt_new_lock() and depends on
115 * getpid() or 'id' argument of the mnt_new_lock() function.
116 *
117 * Returns: unique (per process/thread) path to linkfile.
27c6d415 118 */
68164f6c 119static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
27c6d415 120{
66c33aab 121 return ml ? ml->linkfile : NULL;
27c6d415
KZ
122}
123
124static void mnt_lockalrm_handler(int sig)
125{
126 /* do nothing, say nothing, be nothing */
127}
128
129/*
130 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
131 * fcntl() to avoid never ending waiting.
132 *
192c6aad 133 * Returns: 0 on success, 1 on timeout, -errno on error.
27c6d415 134 */
68164f6c 135static int mnt_wait_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
27c6d415
KZ
136{
137 struct timeval now;
138 struct sigaction sa, osa;
139 int ret = 0;
140
141 gettimeofday(&now, NULL);
142
143 if (now.tv_sec >= maxtime)
144 return 1; /* timeout */
145
146 /* setup ALARM handler -- we don't want to wait forever */
147 sa.sa_flags = 0;
148 sa.sa_handler = mnt_lockalrm_handler;
149 sigfillset (&sa.sa_mask);
150
151 sigaction(SIGALRM, &sa, &osa);
152
3f31a959 153 DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid()));
27c6d415
KZ
154
155 alarm(maxtime - now.tv_sec);
156 if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
157 ret = errno == EINTR ? 1 : -errno;
158 alarm(0);
159
160 /* restore old sigaction */
161 sigaction(SIGALRM, &osa, NULL);
162
3f31a959
KZ
163 DBG(LOCKS, mnt_debug_h(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
164 getpid(), ret));
27c6d415
KZ
165 return ret;
166}
167
168/*
169 * Create the lock file.
170 *
171 * The old code here used flock on a lock file /etc/mtab~ and deleted
172 * this lock file afterwards. However, as rgooch remarks, that has a
173 * race: a second mount may be waiting on the lock and proceed as
174 * soon as the lock file is deleted by the first mount, and immediately
175 * afterwards a third mount comes, creates a new /etc/mtab~, applies
176 * flock to that, and also proceeds, so that the second and third mount
177 * now both are scribbling in /etc/mtab.
178 *
179 * The new code uses a link() instead of a creat(), where we proceed
180 * only if it was us that created the lock, and hence we always have
181 * to delete the lock afterwards. Now the use of flock() is in principle
182 * superfluous, but avoids an arbitrary sleep().
183 *
184 * Where does the link point to? Obvious choices are mtab and mtab~~.
185 * HJLu points out that the latter leads to races. Right now we use
186 * mtab~.<pid> instead.
187 *
188 *
189 * The original mount locking code has used sleep(1) between attempts and
190 * maximal number of attempts has been 5.
191 *
192 * There was very small number of attempts and extremely long waiting (1s)
193 * that is useless on machines with large number of mount processes.
194 *
192c6aad 195 * Now we wait few thousand microseconds between attempts and we have a global
27c6d415
KZ
196 * time limit (30s) rather than limit for number of attempts. The advantage
197 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
198 * number of attempts is not restricted.
199 * -- kzak@redhat.com [Mar-2007]
200 *
201 *
202 * This mtab locking code has been refactored and moved to libmount. The mtab
203 * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and
66c33aab
KZ
204 * backwardly compatible code.
205 *
206 * Don't forget that this code has to be compatible with 3rd party mounts
207 * (/sbin/mount.<foo>) and has to work with NFS.
27c6d415
KZ
208 * -- kzak@redhat.com [May-2009]
209 */
210
211/* maximum seconds between first and last attempt */
212#define MOUNTLOCK_MAXTIME 30
213
214/* sleep time (in microseconds, max=999999) between attempts */
215#define MOUNTLOCK_WAITTIME 5000
216
66c33aab
KZ
217/**
218 * mnt_unlock_file:
219 * @ml: lock struct
220 *
221 * Unlocks the file. The function could be called independently on the
222 * lock status (for example from exit(3)).
223 */
68164f6c 224void mnt_unlock_file(struct libmnt_lock *ml)
27c6d415
KZ
225{
226 if (!ml)
227 return;
228
27c6d415
KZ
229
230 if (ml->locked == 0 && ml->lockfile && ml->linkfile)
231 {
232 /* We have (probably) all files, but we don't own the lock,
233 * Really? Check it! Maybe ml->locked wasn't set properly
234 * because code was interrupted by signal. Paranoia? Yes.
235 *
236 * We own the lock when linkfile == lockfile.
237 */
238 struct stat lo, li;
239
240 if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
241 lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
242 ml->locked = 1;
243 }
3a5b1b1d
KZ
244
245 DBG(LOCKS, mnt_debug_h(ml, "(%d) %s", getpid(),
246 ml->locked ? "unlocking" : "cleaning"));
247
27c6d415
KZ
248 if (ml->linkfile)
249 unlink(ml->linkfile);
250 if (ml->lockfile_fd >= 0)
251 close(ml->lockfile_fd);
252 if (ml->locked == 1 && ml->lockfile)
253 unlink(ml->lockfile);
254
255 ml->locked = 0;
256 ml->lockfile_fd = -1;
257}
258
259/**
cbec3cbf 260 * mnt_lock_file
68164f6c 261 * @ml: pointer to struct libmnt_lock instance
27c6d415
KZ
262 *
263 * Creates lock file (e.g. /etc/mtab~). Note that this function uses
264 * alarm().
265 *
266 * Your application has to always call mnt_unlock_file() before exit.
267 *
268 * Locking scheme:
269 *
3d735589
KZ
270 * 1. create linkfile (e.g. /etc/mtab~.$PID)
271 * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
27c6d415 272 *
192c6aad
KZ
273 * 3. a) link() success: setups F_SETLK lock (see fcnlt(2))
274 * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2.
27c6d415
KZ
275 *
276 * Example:
277 *
192c6aad
KZ
278 * <informalexample>
279 * <programlisting>
68164f6c 280 * struct libmnt_lock *ml;
27c6d415 281 *
3d735589
KZ
282 * void unlock_fallback(void)
283 * {
284 * if (!ml)
285 * return;
286 * mnt_unlock_file(ml);
287 * mnt_free_lock(ml);
288 * }
27c6d415 289 *
3d735589
KZ
290 * int update_mtab()
291 * {
292 * int sig = 0;
66c33aab 293 * const char *mtab;
27c6d415 294 *
a7fd8a36 295 * if (!(mtab = mnt_get_mtab_path()))
66c33aab
KZ
296 * return 0; // system without mtab
297 * if (!(ml = mnt_new_lock(mtab, 0)))
298 * return -1; // error
27c6d415 299 *
66c33aab 300 * atexit(unlock_fallback);
27c6d415 301 *
3d735589
KZ
302 * if (mnt_lock_file(ml) != 0) {
303 * printf(stderr, "cannot create %s lockfile\n",
304 * mnt_lock_get_lockfile(ml));
305 * return -1;
306 * }
27c6d415 307 *
3d735589 308 * ... modify mtab ...
27c6d415 309 *
3d735589
KZ
310 * mnt_unlock_file(ml);
311 * mnt_free_lock(ml);
312 * ml = NULL;
313 * return 0;
314 * }
192c6aad
KZ
315 * </programlisting>
316 * </informalexample>
27c6d415 317 *
03a8029b
KZ
318 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
319 * of stale lock file).
27c6d415 320 */
68164f6c 321int mnt_lock_file(struct libmnt_lock *ml)
27c6d415 322{
03a8029b 323 int i, rc = -1;
27c6d415
KZ
324 struct timespec waittime;
325 struct timeval maxtime;
326 const char *lockfile, *linkfile;
327
328 if (!ml)
03a8029b 329 return -EINVAL;
27c6d415
KZ
330 if (ml->locked)
331 return 0;
332
333 lockfile = mnt_lock_get_lockfile(ml);
334 if (!lockfile)
03a8029b 335 return -EINVAL;
27c6d415
KZ
336 linkfile = mnt_lock_get_linkfile(ml);
337 if (!linkfile)
03a8029b 338 return -EINVAL;
27c6d415
KZ
339
340 i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
03a8029b
KZ
341 if (i < 0) {
342 /* linkfile does not exist (as a file) and we cannot create it.
343 * Read-only or full filesystem? Too many files open in the system?
344 */
345 if (errno > 0)
346 rc = -errno;
27c6d415 347 goto failed;
03a8029b 348 }
27c6d415
KZ
349 close(i);
350
351 gettimeofday(&maxtime, NULL);
352 maxtime.tv_sec += MOUNTLOCK_MAXTIME;
353
354 waittime.tv_sec = 0;
355 waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
356
357 /* Repeat until it was us who made the link */
358 while (ml->locked == 0) {
359 struct timeval now;
360 struct flock flock;
361 int j;
362
363 j = link(linkfile, lockfile);
364 if (j == 0)
365 ml->locked = 1;
366
03a8029b
KZ
367 if (j < 0 && errno != EEXIST) {
368 if (errno > 0)
369 rc = -errno;
27c6d415 370 goto failed;
03a8029b 371 }
27c6d415
KZ
372 ml->lockfile_fd = open(lockfile, O_WRONLY);
373
374 if (ml->lockfile_fd < 0) {
375 /* Strange... Maybe the file was just deleted? */
376 int errsv = errno;
377 gettimeofday(&now, NULL);
378 if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
379 ml->locked = 0;
380 continue;
381 }
03a8029b
KZ
382 if (errsv > 0)
383 rc = -errsv;
27c6d415
KZ
384 goto failed;
385 }
386
387 flock.l_type = F_WRLCK;
388 flock.l_whence = SEEK_SET;
389 flock.l_start = 0;
390 flock.l_len = 0;
391
392 if (ml->locked) {
393 /* We made the link. Now claim the lock. */
394 if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) {
3f31a959 395 DBG(LOCKS, mnt_debug_h(ml,
27c6d415
KZ
396 "%s: can't F_SETLK lockfile, errno=%d\n",
397 lockfile, errno));
398 /* proceed, since it was us who created the lockfile anyway */
399 }
400 break;
401 } else {
402 /* Someone else made the link. Wait. */
403 int err = mnt_wait_lock(ml, &flock, maxtime.tv_sec);
404
405 if (err == 1) {
3f31a959 406 DBG(LOCKS, mnt_debug_h(ml,
27c6d415
KZ
407 "%s: can't create link: time out (perhaps "
408 "there is a stale lock file?)", lockfile));
03a8029b 409 rc = -ETIMEDOUT;
27c6d415
KZ
410 goto failed;
411
03a8029b
KZ
412 } else if (err < 0) {
413 rc = err;
27c6d415 414 goto failed;
03a8029b 415 }
27c6d415
KZ
416 nanosleep(&waittime, NULL);
417 close(ml->lockfile_fd);
418 ml->lockfile_fd = -1;
419 }
420 }
3f31a959 421 DBG(LOCKS, mnt_debug_h(ml,
1d0cd73f 422 "%s: (%d) successfully locked\n",
66c33aab 423 lockfile, getpid()));
27c6d415
KZ
424 unlink(linkfile);
425 return 0;
426
427failed:
428 mnt_unlock_file(ml);
03a8029b 429 return rc;
27c6d415
KZ
430}
431
432#ifdef TEST_PROGRAM
27c6d415 433
68164f6c 434struct libmnt_lock *lock;
27c6d415
KZ
435
436/*
437 * read number from @filename, increment the number and
438 * write the number back to the file
439 */
440void increment_data(const char *filename, int verbose, int loopno)
441{
442 long num;
443 FILE *f;
444 char buf[256];
445
446 if (!(f = fopen(filename, "r")))
447 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
448
449 if (!fgets(buf, sizeof(buf), f))
450 err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
451
452 fclose(f);
453 num = atol(buf) + 1;
454
455 if (!(f = fopen(filename, "w")))
456 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
457
458 fprintf(f, "%ld", num);
459 fclose(f);
460
461 if (verbose)
462 fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
463 filename, num - 1, num, loopno);
464}
465
466void clean_lock(void)
467{
27c6d415
KZ
468 if (!lock)
469 return;
470 mnt_unlock_file(lock);
471 mnt_free_lock(lock);
472}
473
474void sig_handler(int sig)
475{
476 errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
477}
478
68164f6c 479int test_lock(struct libmnt_test *ts, int argc, char *argv[])
27c6d415 480{
9e10ad3d
KZ
481 time_t synctime = 0;
482 unsigned int usecs;
483 struct timeval tv;
484 const char *datafile = NULL;
485 int verbose = 0, loops = 0, l, idx = 1;
27c6d415 486
66c33aab 487 if (argc < 3)
9e10ad3d 488 return -EINVAL;
27c6d415 489
9e10ad3d
KZ
490 if (strcmp(argv[idx], "--synctime") == 0) {
491 synctime = (time_t) atol(argv[idx + 1]);
492 idx += 2;
493 }
494 if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
27c6d415 495 verbose = 1;
9e10ad3d
KZ
496 idx++;
497 }
498
499 if (idx < argc)
500 datafile = argv[idx++];
501 if (idx < argc)
502 loops = atoi(argv[idx++]);
503
504 if (!datafile || !loops)
505 return -EINVAL;
506
3a5b1b1d
KZ
507 if (verbose)
508 fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
509 getpid(), (int) synctime, datafile, loops);
27c6d415
KZ
510
511 atexit(clean_lock);
512
513 /* be paranoid and call exit() (=clean_lock()) for all signals */
514 {
515 int sig = 0;
516 struct sigaction sa;
517
518 sa.sa_handler = sig_handler;
519 sa.sa_flags = 0;
520 sigfillset(&sa.sa_mask);
521
522 while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
523 sigaction (sig, &sa, (struct sigaction *) 0);
524 }
525
9e10ad3d
KZ
526 /* start the test in exactly defined time */
527 if (synctime) {
528 gettimeofday(&tv, NULL);
529 if (synctime && synctime - tv.tv_sec > 1) {
530 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
531 (1000000UL - tv.tv_usec);
532 usleep(usecs);
533 }
534 }
535
27c6d415 536 for (l = 0; l < loops; l++) {
66c33aab
KZ
537 lock = mnt_new_lock(datafile, 0);
538 if (!lock)
539 return -1;
27c6d415 540
abc3d154 541 if (mnt_lock_file(lock) != 0) {
66c33aab
KZ
542 fprintf(stderr, "%d: failed to lock %s file\n",
543 getpid(), datafile);
27c6d415
KZ
544 return -1;
545 }
546
547 increment_data(datafile, verbose, l);
548
549 mnt_unlock_file(lock);
550 mnt_free_lock(lock);
551 lock = NULL;
9e10ad3d
KZ
552
553 /* The mount command usually finish after mtab update. We
554 * simulate this via short sleep -- it's also enough to make
555 * concurrent processes happy.
556 */
557 if (synctime)
558 usleep(25000);
27c6d415
KZ
559 }
560
561 return 0;
562}
563
192c6aad
KZ
564/*
565 * Note that this test should be executed from a script that creates many
566 * parallel processes, otherwise this test does not make sense.
567 */
27c6d415
KZ
568int main(int argc, char *argv[])
569{
68164f6c 570 struct libmnt_test tss[] = {
9e10ad3d
KZ
571 { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
572 "increment a number in datafile" },
27c6d415
KZ
573 { NULL }
574 };
575
576 return mnt_run_test(tss, argc, argv);
577}
578
579#endif /* TEST_PROGRAM */