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