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