~ubuntu-branches/ubuntu/quantal/util-linux/quantal

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2011-06-20 22:31:50 UTC
  • mfrom: (1.6.3 upstream) (4.5.1 sid)
  • Revision ID: james.westby@ubuntu.com-20110620223150-lz8wrv0946ihcz3z
Tags: 2.19.1-2ubuntu1
* Merge from Debian unstable, remaining changes:
  - Build for multiarch.
  - Add pre-depends on multiarch-support.
  - configure.ac: don't try to be clever about extracting a path name from
    $libdir to append to /usr in a way that's not overridable; instead,
    reuse the built-in configurable libexecdir.
  - Fix up the .pc.in files to know about libexecdir, so our substitutions
    don't leave us with unusable pkg-config files.
  - Install custom blkid.conf to use /dev/.blkid.tab since we don't
    expect device names to survive a reboot
  - Mention mountall(8) in fstab(5) manpages, along with its special
    options.
  - Since upstart is required in Ubuntu, the hwclock.sh init script is not
    called on startup and the hwclockfirst.sh init script is removed.
  - Drop depends on initscripts for the above.
  - Replace hwclock udev rule with an Upstart job.
  - For the case where mount is called with a directory to mount, look
    that directory up in mountall's /lib/init/fstab if we couldn't find
    it mentioned anywhere else.  This means "mount /proc", "mount /sys",
    etc. work.
  - mount.8 points to the cifs-utils package, not the obsolete smbfs one. 
* Dropped changes:
  - mount.preinst: lsb_release has been fixed in lucid and above to be
    usable without configuration, so we don't have to diverge from Debian
    here anymore.
* Changes merged upstream:
  - sfdisk support for '+' with '-N'
  - mount/umount.c: fix a segfault on umount with empty mtab entry
  - Fix arbitrary unmount with fuse security issue

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 */