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