cifs-utils: bump version to 6.10
[cifs-utils.git] / mtab.c
1 /*
2  * mtab locking routines for use with mount.cifs and umount.cifs
3  * Copyright (C) 2008 Jeff Layton (jlayton@samba.org)
4  *
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.
9  *
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.
14  *
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/>.
17 */
18
19 /*
20  * This code was copied from the util-linux-ng sources and modified:
21  *
22  * git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git
23  *
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.
27  *
28  * Jeff Layton <jlayton@samba.org> 
29  */
30
31 #include <unistd.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <sys/time.h>
35 #include <sys/stat.h>
36 #include <time.h>
37 #include <fcntl.h>
38 #include <mntent.h>
39 #include <stdlib.h>
40 #include <signal.h>
41 #include <paths.h>
42 #include "mount.h"
43 #include "config.h"
44
45
46 /* Updating mtab ----------------------------------------------*/
47
48 /* Flag for already existing lock file. */
49 static int we_created_lockfile = 0;
50 static int lockfile_fd = -1;
51
52 /* Flag to indicate that signals have been set up. */
53 static int signals_have_been_setup = 0;
54
55 static void
56 handler (int sig __attribute__((unused))) {
57      exit(EX_USER);
58 }
59
60 static void
61 setlkw_timeout (int sig __attribute__((unused))) {
62      /* nothing, fcntl will fail anyway */
63 }
64
65 /* use monotonic time for timeouts */
66 static struct timeval
67 mono_time(void) {
68         struct timeval ret;
69 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
70         struct timespec ts;
71         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
72                 ret.tv_sec = ts.tv_sec;
73                 ret.tv_usec = ts.tv_nsec/1000;
74                 return ret;
75         }
76 #endif
77         gettimeofday(&ret,NULL);
78         return ret;
79 }
80
81 /*
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.
84  */
85 int
86 mtab_unusable(void)
87 {
88         struct stat mstat;
89
90         if(lstat(_PATH_MOUNTED, &mstat))
91                 return errno;
92         else if (S_ISLNK(mstat.st_mode))
93                 return EMLINK;
94         return 0;
95 }
96
97 /* Remove lock file.  */
98 void
99 unlock_mtab (void) {
100         if (we_created_lockfile) {
101                 close(lockfile_fd);
102                 lockfile_fd = -1;
103                 unlink (_PATH_MOUNTED_LOCK);
104                 we_created_lockfile = 0;
105         }
106 }
107
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(). */
121
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)
127
128 /*
129  * The original mount locking code has used sleep(1) between attempts and
130  * maximal number of attemps has been 5.
131  *
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.
134  *
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.
139  *
140  * -- kzak@redhat.com [2007-Mar-2007]
141  */
142
143 /* maximum seconds between first and last attempt */
144 #define MOUNTLOCK_MAXTIME               30
145
146 /* sleep time (in microseconds, max=999999) between attempts */
147 #define MOUNTLOCK_WAITTIME              5000
148
149 int
150 lock_mtab (void) {
151         int i;
152         struct timespec waittime;
153         struct timeval maxtime;
154         char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
155
156         if (!signals_have_been_setup) {
157                 int sig = 0;
158                 struct sigaction sa;
159
160                 sa.sa_handler = handler;
161                 sa.sa_flags = 0;
162                 sigfillset (&sa.sa_mask);
163
164                 while (sigismember (&sa.sa_mask, ++sig) != -1
165                        && sig != SIGCHLD) {
166                         if (sig == SIGALRM)
167                                 sa.sa_handler = setlkw_timeout;
168                         else
169                                 sa.sa_handler = handler;
170                         sigaction (sig, &sa, (struct sigaction *) 0);
171                 }
172                 signals_have_been_setup = 1;
173         }
174
175         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
176
177         i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
178         if (i < 0) {
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?
182                    Filesystem full? */
183                 return EX_FILEIO;
184         }
185         close(i);
186
187         maxtime = mono_time();
188         maxtime.tv_sec += MOUNTLOCK_MAXTIME;
189
190         waittime.tv_sec = 0;
191         waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
192
193         /* Repeat until it was us who made the link */
194         while (!we_created_lockfile) {
195                 struct timeval now;
196                 struct flock flock;
197                 int errsv, j;
198
199                 j = link(linktargetfile, _PATH_MOUNTED_LOCK);
200                 errsv = errno;
201
202                 if (j == 0)
203                         we_created_lockfile = 1;
204
205                 if (j < 0 && errsv != EEXIST) {
206                         (void) unlink(linktargetfile);
207                         return EX_FILEIO;
208                 }
209
210                 lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
211
212                 if (lockfile_fd < 0) {
213                         /* Strange... Maybe the file was just deleted? */
214                         now = mono_time();
215                         if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
216                                 we_created_lockfile = 0;
217                                 continue;
218                         }
219                         (void) unlink(linktargetfile);
220                         return EX_FILEIO;
221                 }
222
223                 flock.l_type = F_WRLCK;
224                 flock.l_whence = SEEK_SET;
225                 flock.l_start = 0;
226                 flock.l_len = 0;
227
228                 if (j == 0) {
229                         /* We made the link. Now claim the lock. If we can't
230                          * get it, continue anyway
231                          */
232                         fcntl (lockfile_fd, F_SETLK, &flock);
233                         (void) unlink(linktargetfile);
234                 } else {
235                         /* Someone else made the link. Wait. */
236                         now = mono_time();
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);
241                                         return EX_FILEIO;
242                                 }
243                                 alarm(0);
244                                 nanosleep(&waittime, NULL);
245                         } else {
246                                 (void) unlink(linktargetfile);
247                                 return EX_FILEIO;
248                         }
249                         close(lockfile_fd);
250                 }
251         }
252         return 0;
253 }
254
255 /*
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.
259  */
260 int
261 my_endmntent(FILE *stream, off_t size)
262 {
263         int rc, fd;
264
265         fd = fileno(stream);
266         if (fd < 0)
267                 return -EBADF;
268
269         rc = fflush(stream);
270         if (!rc)
271                 rc = fsync(fd);
272
273         /* truncate file back to "size" -- best effort here */
274         if (rc) {
275                 int ignore __attribute__((unused));
276
277                 rc = errno;
278                 ignore = ftruncate(fd, size);
279         }
280
281         endmntent(stream);
282         return rc;
283 }