~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to shlibs/mount/src/lock.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
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: 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
 
 */
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"
32
 
 
33
 
#include "mountP.h"
34
 
 
35
 
/*
36
 
 * lock handler
37
 
 */
38
 
struct libmnt_lock {
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 */
42
 
 
43
 
        int     locked : 1;     /* do we own the lock? */
44
 
        int     sigblock : 1;   /* block signals when locked */
45
 
 
46
 
        sigset_t oldsigmask;
47
 
};
48
 
 
49
 
 
50
 
/**
51
 
 * mnt_new_lock:
52
 
 * @datafile: the file that should be covered by the lock
53
 
 * @id: unique linkfile identifier or 0 (default is getpid())
54
 
 *
55
 
 * Returns: newly allocated lock handler or NULL on case of error.
56
 
 */
57
 
struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
58
 
{
59
 
        struct libmnt_lock *ml = NULL;
60
 
        char *lo = NULL, *ln = NULL;
61
 
 
62
 
        /* lockfile */
63
 
        if (!datafile)
64
 
                return NULL;
65
 
 
66
 
        if (asprintf(&lo, "%s~", datafile) == -1) {
67
 
                lo = NULL;
68
 
                goto err;
69
 
        }
70
 
        if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
71
 
                ln = NULL;
72
 
                goto err;
73
 
        }
74
 
        ml = calloc(1, sizeof(*ml) );
75
 
        if (!ml)
76
 
                goto err;
77
 
 
78
 
        ml->lockfile_fd = -1;
79
 
        ml->linkfile = ln;
80
 
        ml->lockfile = lo;
81
 
 
82
 
        DBG(LOCKS, mnt_debug_h(ml, "alloc: linkfile=%s, lockfile=%s", ln, lo));
83
 
        return ml;
84
 
err:
85
 
        free(lo);
86
 
        free(ln);
87
 
        free(ml);
88
 
        return NULL;
89
 
}
90
 
 
91
 
 
92
 
/**
93
 
 * mnt_free_lock:
94
 
 * @ml: struct libmnt_lock handler
95
 
 *
96
 
 * Deallocates mnt_lock.
97
 
 */
98
 
void mnt_free_lock(struct libmnt_lock *ml)
99
 
{
100
 
        if (!ml)
101
 
                return;
102
 
        DBG(LOCKS, mnt_debug_h(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
103
 
        free(ml->lockfile);
104
 
        free(ml->linkfile);
105
 
        free(ml);
106
 
}
107
 
 
108
 
/**
109
 
 * mnt_lock_block_signals:
110
 
 * @ml: struct libmnt_lock handler
111
 
 * @enable: TRUE/FALSE
112
 
 *
113
 
 * Block/unblock signals when the lock is locked, the signals are not blocked
114
 
 * by default.
115
 
 *
116
 
 * Returns: <0 on error, 0 on success.
117
 
 */
118
 
int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
119
 
{
120
 
        if (!ml)
121
 
                return -EINVAL;
122
 
        DBG(LOCKS, mnt_debug_h(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
123
 
        ml->sigblock = enable ? 1 : 0;
124
 
        return 0;
125
 
}
126
 
 
127
 
/*
128
 
 * Returns path to lockfile.
129
 
 */
130
 
static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
131
 
{
132
 
        return ml ? ml->lockfile : NULL;
133
 
}
134
 
 
135
 
/*
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.
138
 
 *
139
 
 * Returns: unique (per process/thread) path to linkfile.
140
 
 */
141
 
static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
142
 
{
143
 
        return ml ? ml->linkfile : NULL;
144
 
}
145
 
 
146
 
static void mnt_lockalrm_handler(int sig)
147
 
{
148
 
        /* do nothing, say nothing, be nothing */
149
 
}
150
 
 
151
 
/*
152
 
 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
153
 
 * fcntl() to avoid never ending waiting.
154
 
 *
155
 
 * Returns: 0 on success, 1 on timeout, -errno on error.
156
 
 */
157
 
static int mnt_wait_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
158
 
{
159
 
        struct timeval now;
160
 
        struct sigaction sa, osa;
161
 
        int ret = 0;
162
 
 
163
 
        gettimeofday(&now, NULL);
164
 
 
165
 
        if (now.tv_sec >= maxtime)
166
 
                return 1;               /* timeout */
167
 
 
168
 
        /* setup ALARM handler -- we don't want to wait forever */
169
 
        sa.sa_flags = 0;
170
 
        sa.sa_handler = mnt_lockalrm_handler;
171
 
        sigfillset (&sa.sa_mask);
172
 
 
173
 
        sigaction(SIGALRM, &sa, &osa);
174
 
 
175
 
        DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid()));
176
 
 
177
 
        alarm(maxtime - now.tv_sec);
178
 
        if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
179
 
                ret = errno == EINTR ? 1 : -errno;
180
 
        alarm(0);
181
 
 
182
 
        /* restore old sigaction */
183
 
        sigaction(SIGALRM, &osa, NULL);
184
 
 
185
 
        DBG(LOCKS, mnt_debug_h(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
186
 
                                getpid(), ret));
187
 
        return ret;
188
 
}
189
 
 
190
 
/*
191
 
 * Create the lock file.
192
 
 *
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.
200
 
 *
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().
205
 
 *
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.
209
 
 *
210
 
 *
211
 
 * The original mount locking code has used sleep(1) between attempts and
212
 
 * maximal number of attempts has been 5.
213
 
 *
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.
216
 
 *
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]
222
 
 *
223
 
 *
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.
227
 
 *
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]
231
 
 */
232
 
 
233
 
/* maximum seconds between first and last attempt */
234
 
#define MOUNTLOCK_MAXTIME               30
235
 
 
236
 
/* sleep time (in microseconds, max=999999) between attempts */
237
 
#define MOUNTLOCK_WAITTIME              5000
238
 
 
239
 
/**
240
 
 * mnt_unlock_file:
241
 
 * @ml: lock struct
242
 
 *
243
 
 * Unlocks the file. The function could be called independently on the
244
 
 * lock status (for example from exit(3)).
245
 
 */
246
 
void mnt_unlock_file(struct libmnt_lock *ml)
247
 
{
248
 
        if (!ml)
249
 
                return;
250
 
 
251
 
        if (!ml->locked && ml->lockfile && ml->linkfile)
252
 
        {
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.
256
 
                 *
257
 
                 * We own the lock when linkfile == lockfile.
258
 
                 */
259
 
                struct stat lo, li;
260
 
 
261
 
                if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
262
 
                    lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
263
 
                        ml->locked = 1;
264
 
        }
265
 
 
266
 
        DBG(LOCKS, mnt_debug_h(ml, "(%d) %s", getpid(),
267
 
                        ml->locked ? "unlocking" : "cleaning"));
268
 
 
269
 
        if (ml->linkfile)
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));
276
 
        }
277
 
        ml->locked = 0;
278
 
        ml->lockfile_fd = -1;
279
 
 
280
 
        if (ml->sigblock) {
281
 
                DBG(LOCKS, mnt_debug_h(ml, "restoring sigmask"));
282
 
                sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
283
 
        }
284
 
        ml->sigblock = 0;
285
 
}
286
 
 
287
 
/**
288
 
 * mnt_lock_file
289
 
 * @ml: pointer to struct libmnt_lock instance
290
 
 *
291
 
 * Creates lock file (e.g. /etc/mtab~). Note that this function uses
292
 
 * alarm().
293
 
 *
294
 
 * Your application has to always call mnt_unlock_file() before exit.
295
 
 *
296
 
 * Locking scheme:
297
 
 *
298
 
 *   1. create linkfile (e.g. /etc/mtab~.$PID)
299
 
 *   2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
300
 
 *
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.
303
 
 *
304
 
 * Example:
305
 
 *
306
 
 * <informalexample>
307
 
 *   <programlisting>
308
 
 *      struct libmnt_lock *ml;
309
 
 *
310
 
 *      void unlock_fallback(void)
311
 
 *      {
312
 
 *              if (!ml)
313
 
 *                      return;
314
 
 *              mnt_unlock_file(ml);
315
 
 *              mnt_free_lock(ml);
316
 
 *      }
317
 
 *
318
 
 *      int update_mtab()
319
 
 *      {
320
 
 *              int sig = 0;
321
 
 *              const char *mtab;
322
 
 *
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
327
 
 *
328
 
 *              atexit(unlock_fallback);
329
 
 *
330
 
 *              if (mnt_lock_file(ml) != 0) {
331
 
 *                      printf(stderr, "cannot create %s lockfile\n",
332
 
 *                                      mnt_lock_get_lockfile(ml));
333
 
 *                      return -1;
334
 
 *              }
335
 
 *
336
 
 *              ... modify mtab ...
337
 
 *
338
 
 *              mnt_unlock_file(ml);
339
 
 *              mnt_free_lock(ml);
340
 
 *              ml = NULL;
341
 
 *              return 0;
342
 
 *      }
343
 
 *   </programlisting>
344
 
 * </informalexample>
345
 
 *
346
 
 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
347
 
 * of stale lock file).
348
 
 */
349
 
int mnt_lock_file(struct libmnt_lock *ml)
350
 
{
351
 
        int i, rc = -1;
352
 
        struct timespec waittime;
353
 
        struct timeval maxtime;
354
 
        const char *lockfile, *linkfile;
355
 
 
356
 
        if (!ml)
357
 
                return -EINVAL;
358
 
        if (ml->locked)
359
 
                return 0;
360
 
 
361
 
        lockfile = mnt_lock_get_lockfile(ml);
362
 
        if (!lockfile)
363
 
                return -EINVAL;
364
 
        linkfile = mnt_lock_get_linkfile(ml);
365
 
        if (!linkfile)
366
 
                return -EINVAL;
367
 
 
368
 
        if (ml->sigblock) {
369
 
                /*
370
 
                 * Block all signals when locked, mnt_unlock_file() will
371
 
                 * restore the old mask.
372
 
                 */
373
 
                sigset_t sigs;
374
 
 
375
 
                sigemptyset(&ml->oldsigmask);
376
 
                sigfillset(&sigs);
377
 
                sigdelset(&sigs, SIGTRAP);
378
 
                sigdelset(&sigs, SIGALRM);
379
 
                sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
380
 
        }
381
 
 
382
 
        i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
383
 
        if (i < 0) {
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?
386
 
                 */
387
 
                if (errno > 0)
388
 
                        rc = -errno;
389
 
                goto failed;
390
 
        }
391
 
        close(i);
392
 
 
393
 
        gettimeofday(&maxtime, NULL);
394
 
        maxtime.tv_sec += MOUNTLOCK_MAXTIME;
395
 
 
396
 
        waittime.tv_sec = 0;
397
 
        waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
398
 
 
399
 
        /* Repeat until it was us who made the link */
400
 
        while (!ml->locked) {
401
 
                struct timeval now;
402
 
                struct flock flock;
403
 
                int j;
404
 
 
405
 
                j = link(linkfile, lockfile);
406
 
                if (j == 0)
407
 
                        ml->locked = 1;
408
 
 
409
 
                if (j < 0 && errno != EEXIST) {
410
 
                        if (errno > 0)
411
 
                                rc = -errno;
412
 
                        goto failed;
413
 
                }
414
 
                ml->lockfile_fd = open(lockfile, O_WRONLY);
415
 
 
416
 
                if (ml->lockfile_fd < 0) {
417
 
                        /* Strange... Maybe the file was just deleted? */
418
 
                        int errsv = errno;
419
 
                        gettimeofday(&now, NULL);
420
 
                        if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
421
 
                                ml->locked = 0;
422
 
                                continue;
423
 
                        }
424
 
                        if (errsv > 0)
425
 
                                rc = -errsv;
426
 
                        goto failed;
427
 
                }
428
 
 
429
 
                flock.l_type = F_WRLCK;
430
 
                flock.l_whence = SEEK_SET;
431
 
                flock.l_start = 0;
432
 
                flock.l_len = 0;
433
 
 
434
 
                if (ml->locked) {
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",
439
 
                                        lockfile, errno));
440
 
                                /* proceed, since it was us who created the lockfile anyway */
441
 
                        }
442
 
                        break;
443
 
                } else {
444
 
                        /* Someone else made the link. Wait. */
445
 
                        int err = mnt_wait_lock(ml, &flock, maxtime.tv_sec);
446
 
 
447
 
                        if (err == 1) {
448
 
                                DBG(LOCKS, mnt_debug_h(ml,
449
 
                                        "%s: can't create link: time out (perhaps "
450
 
                                        "there is a stale lock file?)", lockfile));
451
 
                                rc = -ETIMEDOUT;
452
 
                                goto failed;
453
 
 
454
 
                        } else if (err < 0) {
455
 
                                rc = err;
456
 
                                goto failed;
457
 
                        }
458
 
                        nanosleep(&waittime, NULL);
459
 
                        close(ml->lockfile_fd);
460
 
                        ml->lockfile_fd = -1;
461
 
                }
462
 
        }
463
 
        DBG(LOCKS, mnt_debug_h(ml, "%s: (%d) successfully locked",
464
 
                                        lockfile, getpid()));
465
 
        unlink(linkfile);
466
 
        return 0;
467
 
 
468
 
failed:
469
 
        mnt_unlock_file(ml);
470
 
        return rc;
471
 
}
472
 
 
473
 
#ifdef TEST_PROGRAM
474
 
#include <err.h>
475
 
 
476
 
struct libmnt_lock *lock;
477
 
 
478
 
/*
479
 
 * read number from @filename, increment the number and
480
 
 * write the number back to the file
481
 
 */
482
 
void increment_data(const char *filename, int verbose, int loopno)
483
 
{
484
 
        long num;
485
 
        FILE *f;
486
 
        char buf[256];
487
 
 
488
 
        if (!(f = fopen(filename, "r")))
489
 
                err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
490
 
 
491
 
        if (!fgets(buf, sizeof(buf), f))
492
 
                err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
493
 
 
494
 
        fclose(f);
495
 
        num = atol(buf) + 1;
496
 
 
497
 
        if (!(f = fopen(filename, "w")))
498
 
                err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
499
 
 
500
 
        fprintf(f, "%ld", num);
501
 
        fclose(f);
502
 
 
503
 
        if (verbose)
504
 
                fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
505
 
                                filename, num - 1, num, loopno);
506
 
}
507
 
 
508
 
void clean_lock(void)
509
 
{
510
 
        if (!lock)
511
 
                return;
512
 
        mnt_unlock_file(lock);
513
 
        mnt_free_lock(lock);
514
 
}
515
 
 
516
 
void sig_handler(int sig)
517
 
{
518
 
        errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
519
 
}
520
 
 
521
 
int test_lock(struct libmnt_test *ts, int argc, char *argv[])
522
 
{
523
 
        time_t synctime = 0;
524
 
        unsigned int usecs;
525
 
        struct timeval tv;
526
 
        const char *datafile = NULL;
527
 
        int verbose = 0, loops = 0, l, idx = 1;
528
 
 
529
 
        if (argc < 3)
530
 
                return -EINVAL;
531
 
 
532
 
        if (strcmp(argv[idx], "--synctime") == 0) {
533
 
                synctime = (time_t) atol(argv[idx + 1]);
534
 
                idx += 2;
535
 
        }
536
 
        if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
537
 
                verbose = 1;
538
 
                idx++;
539
 
        }
540
 
 
541
 
        if (idx < argc)
542
 
                datafile = argv[idx++];
543
 
        if (idx < argc)
544
 
                loops = atoi(argv[idx++]);
545
 
 
546
 
        if (!datafile || !loops)
547
 
                return -EINVAL;
548
 
 
549
 
        if (verbose)
550
 
                fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
551
 
                         getpid(), (int) synctime, datafile, loops);
552
 
 
553
 
        atexit(clean_lock);
554
 
 
555
 
        /* be paranoid and call exit() (=clean_lock()) for all signals */
556
 
        {
557
 
                int sig = 0;
558
 
                struct sigaction sa;
559
 
 
560
 
                sa.sa_handler = sig_handler;
561
 
                sa.sa_flags = 0;
562
 
                sigfillset(&sa.sa_mask);
563
 
 
564
 
                while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
565
 
                        sigaction (sig, &sa, (struct sigaction *) 0);
566
 
        }
567
 
 
568
 
        /* start the test in exactly defined time */
569
 
        if (synctime) {
570
 
                gettimeofday(&tv, NULL);
571
 
                if (synctime && synctime - tv.tv_sec > 1) {
572
 
                        usecs = ((synctime - tv.tv_sec) * 1000000UL) -
573
 
                                                (1000000UL - tv.tv_usec);
574
 
                        usleep(usecs);
575
 
                }
576
 
        }
577
 
 
578
 
        for (l = 0; l < loops; l++) {
579
 
                lock = mnt_new_lock(datafile, 0);
580
 
                if (!lock)
581
 
                        return -1;
582
 
 
583
 
                if (mnt_lock_file(lock) != 0) {
584
 
                        fprintf(stderr, "%d: failed to lock %s file\n",
585
 
                                        getpid(), datafile);
586
 
                        return -1;
587
 
                }
588
 
 
589
 
                increment_data(datafile, verbose, l);
590
 
 
591
 
                mnt_unlock_file(lock);
592
 
                mnt_free_lock(lock);
593
 
                lock = NULL;
594
 
 
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.
598
 
                 */
599
 
                if (synctime)
600
 
                        usleep(25000);
601
 
        }
602
 
 
603
 
        return 0;
604
 
}
605
 
 
606
 
/*
607
 
 * Note that this test should be executed from a script that creates many
608
 
 * parallel processes, otherwise this test does not make sense.
609
 
 */
610
 
int main(int argc, char *argv[])
611
 
{
612
 
        struct libmnt_test tss[] = {
613
 
        { "--lock", test_lock,  " [--synctime <time_t>] [--verbose] <datafile> <loops> "
614
 
                                "increment a number in datafile" },
615
 
        { NULL }
616
 
        };
617
 
 
618
 
        return mnt_run_test(tss, argc, argv);
619
 
}
620
 
 
621
 
#endif /* TEST_PROGRAM */