1*d5c9a868SElliott Hughes /* Copyright 2005,2009,2018 Alain Knaff.
2*d5c9a868SElliott Hughes * This file is part of mtools.
3*d5c9a868SElliott Hughes *
4*d5c9a868SElliott Hughes * Mtools is free software: you can redistribute it and/or modify
5*d5c9a868SElliott Hughes * it under the terms of the GNU General Public License as published by
6*d5c9a868SElliott Hughes * the Free Software Foundation, either version 3 of the License, or
7*d5c9a868SElliott Hughes * (at your option) any later version.
8*d5c9a868SElliott Hughes *
9*d5c9a868SElliott Hughes * Mtools is distributed in the hope that it will be useful,
10*d5c9a868SElliott Hughes * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*d5c9a868SElliott Hughes * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*d5c9a868SElliott Hughes * GNU General Public License for more details.
13*d5c9a868SElliott Hughes *
14*d5c9a868SElliott Hughes * You should have received a copy of the GNU General Public License
15*d5c9a868SElliott Hughes * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16*d5c9a868SElliott Hughes *
17*d5c9a868SElliott Hughes * Create an advisory lock on the device to prevent concurrent writes.
18*d5c9a868SElliott Hughes * Uses either lockf, flock, or fcntl locking methods. See the Makefile
19*d5c9a868SElliott Hughes * and the Configure files for how to specify the proper method.
20*d5c9a868SElliott Hughes */
21*d5c9a868SElliott Hughes
22*d5c9a868SElliott Hughes #include "sysincludes.h"
23*d5c9a868SElliott Hughes #include "mtools.h"
24*d5c9a868SElliott Hughes #include "lockdev.h"
25*d5c9a868SElliott Hughes
26*d5c9a868SElliott Hughes #if (defined HAVE_SIGACTION && defined HAVE_ALARM)
27*d5c9a868SElliott Hughes # define ALRM
28*d5c9a868SElliott Hughes #endif
29*d5c9a868SElliott Hughes
30*d5c9a868SElliott Hughes
31*d5c9a868SElliott Hughes #if (defined(HAVE_FLOCK) && defined (LOCK_EX) && (defined(LOCK_NB) || defined(ALRM)))
32*d5c9a868SElliott Hughes
33*d5c9a868SElliott Hughes # ifdef ALRM
34*d5c9a868SElliott Hughes # define USE_FLOCK_W
35*d5c9a868SElliott Hughes # else
36*d5c9a868SElliott Hughes # define USE_FLOCK
37*d5c9a868SElliott Hughes # endif
38*d5c9a868SElliott Hughes
39*d5c9a868SElliott Hughes #else /* FLOCK */
40*d5c9a868SElliott Hughes
41*d5c9a868SElliott Hughes #if (defined(HAVE_LOCKF) && (defined(F_TLOCK) || defined(ALRM)))
42*d5c9a868SElliott Hughes
43*d5c9a868SElliott Hughes # ifdef ALRM
44*d5c9a868SElliott Hughes # define USE_LOCKF_W
45*d5c9a868SElliott Hughes # else
46*d5c9a868SElliott Hughes # define USE_LOCKF
47*d5c9a868SElliott Hughes # endif
48*d5c9a868SElliott Hughes
49*d5c9a868SElliott Hughes #else /* LOCKF */
50*d5c9a868SElliott Hughes
51*d5c9a868SElliott Hughes #if (defined(F_SETLK) && defined(F_WRLCK))
52*d5c9a868SElliott Hughes
53*d5c9a868SElliott Hughes # if (defined ALRM && defined F_SETLKW)
54*d5c9a868SElliott Hughes # define USE_SETLK_W
55*d5c9a868SElliott Hughes # else
56*d5c9a868SElliott Hughes # define USE_SETLK_W
57*d5c9a868SElliott Hughes # endif
58*d5c9a868SElliott Hughes
59*d5c9a868SElliott Hughes #else
60*d5c9a868SElliott Hughes
61*d5c9a868SElliott Hughes #endif /* FCNTL */
62*d5c9a868SElliott Hughes #endif /* LOCKF */
63*d5c9a868SElliott Hughes #endif /* FLOCK */
64*d5c9a868SElliott Hughes
65*d5c9a868SElliott Hughes #if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
alrm(int a UNUSEDP)66*d5c9a868SElliott Hughes static void alrm(int a UNUSEDP) {
67*d5c9a868SElliott Hughes }
68*d5c9a868SElliott Hughes #endif
69*d5c9a868SElliott Hughes
lock_dev(int fd,int mode,struct device * dev)70*d5c9a868SElliott Hughes int lock_dev(int fd, int mode, struct device *dev)
71*d5c9a868SElliott Hughes {
72*d5c9a868SElliott Hughes unsigned int retries = 0;
73*d5c9a868SElliott Hughes if(IS_NOLOCK(dev))
74*d5c9a868SElliott Hughes return 0;
75*d5c9a868SElliott Hughes
76*d5c9a868SElliott Hughes while(1) {
77*d5c9a868SElliott Hughes int ret=0;
78*d5c9a868SElliott Hughes #if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
79*d5c9a868SElliott Hughes struct sigaction alrm_action, old_alrm_action;
80*d5c9a868SElliott Hughes unsigned int old_alrm = alarm(0);
81*d5c9a868SElliott Hughes memset(&alrm_action, 0, sizeof(alrm_action));
82*d5c9a868SElliott Hughes alrm_action.sa_handler = alrm;
83*d5c9a868SElliott Hughes alrm_action.sa_flags = 0;
84*d5c9a868SElliott Hughes sigaction(SIGALRM, &alrm_action, &old_alrm_action);
85*d5c9a868SElliott Hughes alarm(mtools_lock_timeout);
86*d5c9a868SElliott Hughes #endif
87*d5c9a868SElliott Hughes
88*d5c9a868SElliott Hughes #ifdef USE_FLOCK
89*d5c9a868SElliott Hughes ret = flock(fd, (mode ? LOCK_EX : LOCK_SH)|LOCK_NB);
90*d5c9a868SElliott Hughes #endif
91*d5c9a868SElliott Hughes
92*d5c9a868SElliott Hughes #ifdef USE_FLOCK_W
93*d5c9a868SElliott Hughes ret = flock(fd, (mode ? LOCK_EX : LOCK_SH));
94*d5c9a868SElliott Hughes #endif
95*d5c9a868SElliott Hughes
96*d5c9a868SElliott Hughes #if (defined(USE_LOCKF) || defined(USE_LOCKF_W))
97*d5c9a868SElliott Hughes if(mode)
98*d5c9a868SElliott Hughes # ifdef USE_LOCKF
99*d5c9a868SElliott Hughes ret = lockf(fd, F_TLOCK, 0);
100*d5c9a868SElliott Hughes # else
101*d5c9a868SElliott Hughes ret = lockf(fd, F_LOCK, 0);
102*d5c9a868SElliott Hughes # endif
103*d5c9a868SElliott Hughes else
104*d5c9a868SElliott Hughes ret = 0;
105*d5c9a868SElliott Hughes #endif
106*d5c9a868SElliott Hughes
107*d5c9a868SElliott Hughes #if (defined(USE_SETLK) || defined(USE_SETLK_W))
108*d5c9a868SElliott Hughes {
109*d5c9a868SElliott Hughes struct flock flk;
110*d5c9a868SElliott Hughes flk.l_type = mode ? F_WRLCK : F_RDLCK;
111*d5c9a868SElliott Hughes flk.l_whence = 0;
112*d5c9a868SElliott Hughes flk.l_start = 0L;
113*d5c9a868SElliott Hughes flk.l_len = 0L;
114*d5c9a868SElliott Hughes
115*d5c9a868SElliott Hughes # ifdef USE_SETLK_W
116*d5c9a868SElliott Hughes ret = fcntl(fd, F_SETLKW, &flk);
117*d5c9a868SElliott Hughes # else
118*d5c9a868SElliott Hughes ret = fcntl(fd, F_SETLK, &flk);
119*d5c9a868SElliott Hughes # endif
120*d5c9a868SElliott Hughes }
121*d5c9a868SElliott Hughes #endif
122*d5c9a868SElliott Hughes
123*d5c9a868SElliott Hughes #if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
124*d5c9a868SElliott Hughes /* Cancel the alarm */
125*d5c9a868SElliott Hughes sigaction(SIGALRM, &old_alrm_action, NULL);
126*d5c9a868SElliott Hughes alarm(old_alrm);
127*d5c9a868SElliott Hughes #endif
128*d5c9a868SElliott Hughes
129*d5c9a868SElliott Hughes if(ret < 0) {
130*d5c9a868SElliott Hughes #if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
131*d5c9a868SElliott Hughes /* ALARM fired ==> this means we are still locked */
132*d5c9a868SElliott Hughes if(errno == EINTR) {
133*d5c9a868SElliott Hughes return 1;
134*d5c9a868SElliott Hughes }
135*d5c9a868SElliott Hughes #endif
136*d5c9a868SElliott Hughes
137*d5c9a868SElliott Hughes if(
138*d5c9a868SElliott Hughes #ifdef EWOULDBLOCK
139*d5c9a868SElliott Hughes (errno != EWOULDBLOCK)
140*d5c9a868SElliott Hughes #else
141*d5c9a868SElliott Hughes 1
142*d5c9a868SElliott Hughes #endif
143*d5c9a868SElliott Hughes &&
144*d5c9a868SElliott Hughes #ifdef EAGAIN
145*d5c9a868SElliott Hughes (errno != EAGAIN)
146*d5c9a868SElliott Hughes #else
147*d5c9a868SElliott Hughes 1
148*d5c9a868SElliott Hughes #endif
149*d5c9a868SElliott Hughes &&
150*d5c9a868SElliott Hughes #ifdef EINTR
151*d5c9a868SElliott Hughes (errno != EINTR)
152*d5c9a868SElliott Hughes #else
153*d5c9a868SElliott Hughes 1
154*d5c9a868SElliott Hughes #endif
155*d5c9a868SElliott Hughes ) {
156*d5c9a868SElliott Hughes /* Error other than simply being locked */
157*d5c9a868SElliott Hughes return -1;
158*d5c9a868SElliott Hughes }
159*d5c9a868SElliott Hughes /* Locked ==> continue until timeout */
160*d5c9a868SElliott Hughes } else /* no error => we got the lock! */
161*d5c9a868SElliott Hughes return 0;
162*d5c9a868SElliott Hughes
163*d5c9a868SElliott Hughes #ifdef HAVE_USLEEP
164*d5c9a868SElliott Hughes if(retries++ < mtools_lock_timeout * 10)
165*d5c9a868SElliott Hughes usleep(100000);
166*d5c9a868SElliott Hughes #else
167*d5c9a868SElliott Hughes if(retries++ < mtools_lock_timeout)
168*d5c9a868SElliott Hughes sleep(1);
169*d5c9a868SElliott Hughes #endif
170*d5c9a868SElliott Hughes else
171*d5c9a868SElliott Hughes /* waited for too long => give up */
172*d5c9a868SElliott Hughes return 1;
173*d5c9a868SElliott Hughes }
174*d5c9a868SElliott Hughes }
175