cifs.idmap: fix endianness on SIDs before sending to kernel
[jlayton/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 "mount.h"
42 #include "config.h"
43
44
45 /* Updating mtab ----------------------------------------------*/
46
47 /* Flag for already existing lock file. */
48 static int we_created_lockfile = 0;
49 static int lockfile_fd = -1;
50
51 /* Flag to indicate that signals have been set up. */
52 static int signals_have_been_setup = 0;
53
54 static void
55 handler (int sig __attribute__((unused))) {
56      exit(EX_USER);
57 }
58
59 static void
60 setlkw_timeout (int sig __attribute__((unused))) {
61      /* nothing, fcntl will fail anyway */
62 }
63
64 /* use monotonic time for timeouts */
65 struct timeval
66 mono_time(void) {
67         struct timeval ret;
68 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
69         struct timespec ts;
70         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
71                 ret.tv_sec = ts.tv_sec;
72                 ret.tv_usec = ts.tv_nsec/1000;
73                 return ret;
74         }
75 #endif
76         gettimeofday(&ret,NULL);
77         return ret;
78 }
79
80 /*
81  * See if mtab is present and whether it's a symlink. Returns errno from stat()
82  * call or EMLINK if it's a symlink.
83  */
84 int
85 mtab_unusable(void)
86 {
87         struct stat mstat;
88
89         if(lstat(_PATH_MOUNTED, &mstat))
90                 return errno;
91         else if (S_ISLNK(mstat.st_mode))
92                 return EMLINK;
93         return 0;
94 }
95
96 /* Remove lock file.  */
97 void
98 unlock_mtab (void) {
99         if (we_created_lockfile) {
100                 close(lockfile_fd);
101                 lockfile_fd = -1;
102                 unlink (_PATH_MOUNTED_LOCK);
103                 we_created_lockfile = 0;
104         }
105 }
106
107 /* Create the lock file.
108    The lock file will be removed if we catch a signal or when we exit. */
109 /* The old code here used flock on a lock file /etc/mtab~ and deleted
110    this lock file afterwards. However, as rgooch remarks, that has a
111    race: a second mount may be waiting on the lock and proceed as
112    soon as the lock file is deleted by the first mount, and immediately
113    afterwards a third mount comes, creates a new /etc/mtab~, applies
114    flock to that, and also proceeds, so that the second and third mount
115    now both are scribbling in /etc/mtab.
116    The new code uses a link() instead of a creat(), where we proceed
117    only if it was us that created the lock, and hence we always have
118    to delete the lock afterwards. Now the use of flock() is in principle
119    superfluous, but avoids an arbitrary sleep(). */
120
121 /* Where does the link point to? Obvious choices are mtab and mtab~~.
122    HJLu points out that the latter leads to races. Right now we use
123    mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
124 #define MOUNTLOCK_LINKTARGET            _PATH_MOUNTED_LOCK "%d"
125 #define MOUNTLOCK_LINKTARGET_LTH        (sizeof(_PATH_MOUNTED_LOCK)+20)
126
127 /*
128  * The original mount locking code has used sleep(1) between attempts and
129  * maximal number of attemps has been 5.
130  *
131  * There was very small number of attempts and extremely long waiting (1s)
132  * that is useless on machines with large number of concurret mount processes.
133  *
134  * Now we wait few thousand microseconds between attempts and we have global
135  * time limit (30s) rather than limit for number of attempts. The advantage
136  * is that this method also counts time which we spend in fcntl(F_SETLKW) and
137  * number of attempts is not so much restricted.
138  *
139  * -- kzak@redhat.com [2007-Mar-2007]
140  */
141
142 /* maximum seconds between first and last attempt */
143 #define MOUNTLOCK_MAXTIME               30
144
145 /* sleep time (in microseconds, max=999999) between attempts */
146 #define MOUNTLOCK_WAITTIME              5000
147
148 int
149 lock_mtab (void) {
150         int i;
151         struct timespec waittime;
152         struct timeval maxtime;
153         char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
154
155         if (!signals_have_been_setup) {
156                 int sig = 0;
157                 struct sigaction sa;
158
159                 sa.sa_handler = handler;
160                 sa.sa_flags = 0;
161                 sigfillset (&sa.sa_mask);
162
163                 while (sigismember (&sa.sa_mask, ++sig) != -1
164                        && sig != SIGCHLD) {
165                         if (sig == SIGALRM)
166                                 sa.sa_handler = setlkw_timeout;
167                         else
168                                 sa.sa_handler = handler;
169                         sigaction (sig, &sa, (struct sigaction *) 0);
170                 }
171                 signals_have_been_setup = 1;
172         }
173
174         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
175
176         i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
177         if (i < 0) {
178                 /* linktargetfile does not exist (as a file)
179                    and we cannot create it. Read-only filesystem?
180                    Too many files open in the system?
181                    Filesystem full? */
182                 return EX_FILEIO;
183         }
184         close(i);
185
186         maxtime = mono_time();
187         maxtime.tv_sec += MOUNTLOCK_MAXTIME;
188
189         waittime.tv_sec = 0;
190         waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
191
192         /* Repeat until it was us who made the link */
193         while (!we_created_lockfile) {
194                 struct timeval now;
195                 struct flock flock;
196                 int errsv, j;
197
198                 j = link(linktargetfile, _PATH_MOUNTED_LOCK);
199                 errsv = errno;
200
201                 if (j == 0)
202                         we_created_lockfile = 1;
203
204                 if (j < 0 && errsv != EEXIST) {
205                         (void) unlink(linktargetfile);
206                         return EX_FILEIO;
207                 }
208
209                 lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
210
211                 if (lockfile_fd < 0) {
212                         /* Strange... Maybe the file was just deleted? */
213                         now = mono_time();
214                         if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
215                                 we_created_lockfile = 0;
216                                 continue;
217                         }
218                         (void) unlink(linktargetfile);
219                         return EX_FILEIO;
220                 }
221
222                 flock.l_type = F_WRLCK;
223                 flock.l_whence = SEEK_SET;
224                 flock.l_start = 0;
225                 flock.l_len = 0;
226
227                 if (j == 0) {
228                         /* We made the link. Now claim the lock. If we can't
229                          * get it, continue anyway
230                          */
231                         fcntl (lockfile_fd, F_SETLK, &flock);
232                         (void) unlink(linktargetfile);
233                 } else {
234                         /* Someone else made the link. Wait. */
235                         now = mono_time();
236                         if (now.tv_sec < maxtime.tv_sec) {
237                                 alarm(maxtime.tv_sec - now.tv_sec);
238                                 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
239                                         (void) unlink(linktargetfile);
240                                         return EX_FILEIO;
241                                 }
242                                 alarm(0);
243                                 nanosleep(&waittime, NULL);
244                         } else {
245                                 (void) unlink(linktargetfile);
246                                 return EX_FILEIO;
247                         }
248                         close(lockfile_fd);
249                 }
250         }
251         return 0;
252 }
253
254 /*
255  * Call fflush and fsync on the mtab, and then endmntent. If either fflush
256  * or fsync fails, then truncate the file back to "size". endmntent is called
257  * unconditionally, and the errno (if any) from fflush and fsync are returned.
258  */
259 int
260 my_endmntent(FILE *stream, off_t size)
261 {
262         int rc, fd;
263
264         fd = fileno(stream);
265         if (fd < 0)
266                 return -EBADF;
267
268         rc = fflush(stream);
269         if (!rc)
270                 rc = fsync(fd);
271
272         /* truncate file back to "size" -- best effort here */
273         if (rc) {
274                 int ignore __attribute__((unused));
275
276                 rc = errno;
277                 ignore = ftruncate(fd, size);
278         }
279
280         endmntent(stream);
281         return rc;
282 }