2 * mtab locking routines for use with mount.cifs and umount.cifs
3 * Copyright (C) 2008 Jeff Layton (jlayton@samba.org)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * This code was copied from the util-linux-ng sources and modified:
22 * git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git
24 * ...specifically from mount/fstab.c. That file has no explicit license. The
25 * "default" license for anything in that tree is apparently GPLv2+, so I
26 * believe we're OK to copy it here.
28 * Jeff Layton <jlayton@samba.org>
46 /* Updating mtab ----------------------------------------------*/
48 /* Flag for already existing lock file. */
49 static int we_created_lockfile = 0;
50 static int lockfile_fd = -1;
52 /* Flag to indicate that signals have been set up. */
53 static int signals_have_been_setup = 0;
56 handler (int sig __attribute__((unused))) {
61 setlkw_timeout (int sig __attribute__((unused))) {
62 /* nothing, fcntl will fail anyway */
65 /* use monotonic time for timeouts */
69 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
71 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
72 ret.tv_sec = ts.tv_sec;
73 ret.tv_usec = ts.tv_nsec/1000;
77 gettimeofday(&ret,NULL);
82 * See if mtab is present and whether it's a symlink. Returns errno from stat()
83 * call or EMLINK if it's a symlink.
90 if(lstat(_PATH_MOUNTED, &mstat))
92 else if (S_ISLNK(mstat.st_mode))
97 /* Remove lock file. */
100 if (we_created_lockfile) {
103 unlink (_PATH_MOUNTED_LOCK);
104 we_created_lockfile = 0;
108 /* Create the lock file.
109 The lock file will be removed if we catch a signal or when we exit. */
110 /* The old code here used flock on a lock file /etc/mtab~ and deleted
111 this lock file afterwards. However, as rgooch remarks, that has a
112 race: a second mount may be waiting on the lock and proceed as
113 soon as the lock file is deleted by the first mount, and immediately
114 afterwards a third mount comes, creates a new /etc/mtab~, applies
115 flock to that, and also proceeds, so that the second and third mount
116 now both are scribbling in /etc/mtab.
117 The new code uses a link() instead of a creat(), where we proceed
118 only if it was us that created the lock, and hence we always have
119 to delete the lock afterwards. Now the use of flock() is in principle
120 superfluous, but avoids an arbitrary sleep(). */
122 /* Where does the link point to? Obvious choices are mtab and mtab~~.
123 HJLu points out that the latter leads to races. Right now we use
124 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
125 #define MOUNTLOCK_LINKTARGET _PATH_MOUNTED_LOCK "%d"
126 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(_PATH_MOUNTED_LOCK)+20)
129 * The original mount locking code has used sleep(1) between attempts and
130 * maximal number of attemps has been 5.
132 * There was very small number of attempts and extremely long waiting (1s)
133 * that is useless on machines with large number of concurret mount processes.
135 * Now we wait few thousand microseconds between attempts and we have global
136 * time limit (30s) rather than limit for number of attempts. The advantage
137 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
138 * number of attempts is not so much restricted.
140 * -- kzak@redhat.com [2007-Mar-2007]
143 /* maximum seconds between first and last attempt */
144 #define MOUNTLOCK_MAXTIME 30
146 /* sleep time (in microseconds, max=999999) between attempts */
147 #define MOUNTLOCK_WAITTIME 5000
152 struct timespec waittime;
153 struct timeval maxtime;
154 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
156 if (!signals_have_been_setup) {
160 sa.sa_handler = handler;
162 sigfillset (&sa.sa_mask);
164 while (sigismember (&sa.sa_mask, ++sig) != -1
167 sa.sa_handler = setlkw_timeout;
169 sa.sa_handler = handler;
170 sigaction (sig, &sa, (struct sigaction *) 0);
172 signals_have_been_setup = 1;
175 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
177 i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
179 /* linktargetfile does not exist (as a file)
180 and we cannot create it. Read-only filesystem?
181 Too many files open in the system?
187 maxtime = mono_time();
188 maxtime.tv_sec += MOUNTLOCK_MAXTIME;
191 waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
193 /* Repeat until it was us who made the link */
194 while (!we_created_lockfile) {
199 j = link(linktargetfile, _PATH_MOUNTED_LOCK);
203 we_created_lockfile = 1;
205 if (j < 0 && errsv != EEXIST) {
206 (void) unlink(linktargetfile);
210 lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
212 if (lockfile_fd < 0) {
213 /* Strange... Maybe the file was just deleted? */
215 if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
216 we_created_lockfile = 0;
219 (void) unlink(linktargetfile);
223 flock.l_type = F_WRLCK;
224 flock.l_whence = SEEK_SET;
229 /* We made the link. Now claim the lock. If we can't
230 * get it, continue anyway
232 fcntl (lockfile_fd, F_SETLK, &flock);
233 (void) unlink(linktargetfile);
235 /* Someone else made the link. Wait. */
237 if (now.tv_sec < maxtime.tv_sec) {
238 alarm(maxtime.tv_sec - now.tv_sec);
239 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
240 (void) unlink(linktargetfile);
244 nanosleep(&waittime, NULL);
246 (void) unlink(linktargetfile);
256 * Call fflush and fsync on the mtab, and then endmntent. If either fflush
257 * or fsync fails, then truncate the file back to "size". endmntent is called
258 * unconditionally, and the errno (if any) from fflush and fsync are returned.
261 my_endmntent(FILE *stream, off_t size)
273 /* truncate file back to "size" -- best effort here */
275 int ignore __attribute__((unused));
278 ignore = ftruncate(fd, size);