common/xfs: extract minimum log size message from mkfs correctly
[xfstests-dev.git] / src / t_mtab.c
1 /*
2  * Test program based on Linux mount(8) source attempting to
3  * trigger a suspected problem in rename(2) code paths - its
4  * symptoms have been multiple mtab entries in /etc... hence
5  * use of the actual mount code here.
6  */
7
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <signal.h>
15 #include <sys/stat.h>
16 #include <mntent.h>
17 #include <limits.h>
18
19 #define LOCK_TIMEOUT    10
20 #define _(x)            (x)
21
22 static char *mounted = "t_mtab";
23 static char *mounted_lock = "t_mtab~";
24 static char *mounted_temp = "t_mtab.tmp";
25
26 /* Updating mtab ----------------------------------------------*/
27
28 /* Flag for already existing lock file. */
29 static int we_created_lockfile = 0;
30
31 /* Flag to indicate that signals have been set up. */
32 static int signals_have_been_setup = 0;
33
34 /* Ensure that the lock is released if we are interrupted.  */
35 static void
36 handler (int sig) {
37     fprintf(stderr, "%s\n", strsignal(sig));
38     exit(1);
39 }
40
41 static void
42 setlkw_timeout (int sig) {
43      /* nothing, fcntl will fail anyway */
44 }
45
46 /* Create the lock file.
47    The lock file will be removed if we catch a signal or when we exit. */
48 /* The old code here used flock on a lock file /etc/mtab~ and deleted
49    this lock file afterwards. However, as rgooch remarks, that has a
50    race: a second mount may be waiting on the lock and proceed as
51    soon as the lock file is deleted by the first mount, and immediately
52    afterwards a third mount comes, creates a new /etc/mtab~, applies
53    flock to that, and also proceeds, so that the second and third mount
54    now both are scribbling in /etc/mtab.
55    The new code uses a link() instead of a creat(), where we proceed
56    only if it was us that created the lock, and hence we always have
57    to delete the lock afterwards. Now the use of flock() is in principle
58    superfluous, but avoids an arbitrary sleep(). */
59
60 void
61 lock_mtab (void) {
62 #if 0   /* nathans: dont limit, we are forcing lots of parallel accesses */
63         int tries = 3;
64 #endif
65         char linktargetfile[PATH_MAX + 20];
66
67         if (!signals_have_been_setup) {
68                 int sig = 0;
69                 struct sigaction sa;
70
71                 sa.sa_handler = handler;
72                 sa.sa_flags = 0;
73                 sigfillset (&sa.sa_mask);
74   
75                 while (sigismember (&sa.sa_mask, ++sig) != -1
76                        && sig != SIGCHLD) {
77                         if (sig == SIGALRM)
78                                 sa.sa_handler = setlkw_timeout;
79                         else
80                                 sa.sa_handler = handler;
81                         sigaction (sig, &sa, (struct sigaction *) 0);
82                 }
83                 signals_have_been_setup = 1;
84         }
85
86         /* use 20 as upper bound for the length of %d output */
87         snprintf(linktargetfile, PATH_MAX+20, "%s%d", mounted_lock, getpid());
88
89         /* Repeat until it was us who made the link */
90         while (!we_created_lockfile) {
91                 struct flock flock;
92                 int fd, errsv, i, j;
93
94                 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
95                 if (i < 0) {
96                         int errsv = errno;
97                         /* linktargetfile does not exist (as a file)
98                            and we cannot create it. Read-only filesystem?
99                            Too many files open in the system?
100                            Filesystem full? */
101                         fprintf(stderr, "can't create lock file %s: %s "
102                              "(use -n flag to override)\n",
103                              linktargetfile, strerror (errsv));
104                         exit(1);
105                 }
106                 close(i);
107
108                 j = link(linktargetfile, mounted_lock);
109                 errsv = errno;
110
111                 (void) unlink(linktargetfile);
112
113                 if (j < 0 && errsv != EEXIST) {
114                         fprintf(stderr, "can't link lock file %s: %s "
115                              "(use -n flag to override)\n",
116                              mounted_lock, strerror (errsv));
117                         exit(1);
118                 }
119
120                 fd = open (mounted_lock, O_WRONLY);
121
122                 if (fd < 0) {
123                         int errsv = errno;
124                         /* Strange... Maybe the file was just deleted? */
125 #if 0   /* nathans: dont limit, we are forcing lots of parallel accesses */
126                         if (errno == ENOENT && tries-- > 0)
127 #endif
128                         if (errno == ENOENT)
129                                 continue;
130                         fprintf(stderr, "can't open lock file %s: %s\n",
131                              mounted_lock, strerror (errsv));
132                         exit(1);
133                 }
134
135                 flock.l_type = F_WRLCK;
136                 flock.l_whence = SEEK_SET;
137                 flock.l_start = 0;
138                 flock.l_len = 0;
139
140                 if (j == 0) {
141                         /* We made the link. Now claim the lock. */
142                         if (fcntl (fd, F_SETLK, &flock) == -1 &&
143                             errno != EBUSY && errno != EAGAIN) {
144                                 int errsv = errno;
145                                 printf(_("Can't lock lock file %s: %s\n"),
146                                            mounted_lock, strerror (errsv));
147                                 /* proceed anyway */
148                         }
149                         we_created_lockfile = 1;
150                 } else {
151 #if 0   /* nathans: dont limit, we are forcing lots of parallel accesses */
152                         static int tries = 0;
153 #endif
154
155                         /* Someone else made the link. Wait. */
156                         alarm(LOCK_TIMEOUT);
157                         if (fcntl (fd, F_SETLKW, &flock) == -1 &&
158                             errno != EBUSY && errno != EAGAIN) {
159                                 int errsv = errno;
160                                 fprintf(stderr, "can't lock lock file %s: %s\n",
161                                      mounted_lock, (errno == EINTR) ?
162                                      _("timed out") : strerror (errsv));
163                                 exit(1);
164                         }
165                         alarm(0);
166 #if 0   /* nathans: dont limit, we are forcing lots of parallel accesses */
167                         /* Limit the number of iterations - maybe there
168                            still is some old /etc/mtab~ */
169                         if (tries++ > 3) {
170                                 if (tries > 5) {
171                                         fprintf(stderr, "Cant create link %s\n"
172                                             "Perhaps there is a stale lock file?\n",
173                                              mounted_lock);
174                                         exit(1);
175                                 }
176                                 sleep(1);
177                         }
178 #endif
179                 }
180                 close (fd);
181         }
182 }
183
184 /* Remove lock file.  */
185 void
186 unlock_mtab (void) {
187         int ret;
188         if (we_created_lockfile) {
189                 ret = unlink (mounted_lock);
190                 if (ret) {
191                         fprintf(stderr, "Cannot remove lock file: %s\n", strerror(errno));
192                         exit(1);
193                 } else {
194                         we_created_lockfile = 0;
195                 }
196         }
197 }
198
199 /*
200  * Update the mtab.
201  *  Used by umount with null INSTEAD: remove the last DIR entry.
202  *  Used by mount upon a remount: update option part,
203  *   and complain if a wrong device or type was given.
204  *   [Note that often a remount will be a rw remount of /
205  *    where there was no entry before, and we'll have to believe
206  *    the values given in INSTEAD.]
207  */
208
209 void
210 update_mtab (void)
211 {
212         FILE *mntent_fp, *mftmp;
213         char buffer[4096];
214         int size;
215
216         lock_mtab();
217
218         /* having locked mtab, read it again & write to mtemp */
219         mntent_fp = fopen(mounted, "r");
220         if (!mntent_fp) {
221                 fprintf(stderr, "cannot open %s for reading\n", mounted);
222                 exit(1);
223         }
224         mftmp = fopen(mounted_temp, "w");
225         if (!mftmp) {
226                 fprintf(stderr, "cannot open %s for writing\n", mounted_temp);
227                 exit(1);
228         }
229         while ((size = read(fileno(mntent_fp), buffer, sizeof(buffer))) > 0) {
230                 if (write(fileno(mftmp), buffer, size) < 0) {
231                         fprintf(stderr, "write failure: %s\n", strerror(errno));
232                         exit(1);
233                 }
234         }
235         if (size < 0) {
236                 fprintf(stderr, "read failure: %s\n", strerror(errno));
237                 exit(1);
238         }
239         fclose(mntent_fp);
240
241         if (fchmod (fileno (mftmp),
242                     S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
243                 int errsv = errno;
244                 fprintf(stderr, _("error changing mode of %s: %s\n"),
245                         mounted_temp, strerror (errsv));
246         }
247         fclose(mftmp);
248
249         { /*
250            * If mount is setuid and some non-root user mounts sth,
251            * then mtab.tmp might get the group of this user. Copy uid/gid
252            * from the present mtab before renaming.
253            */
254             struct stat sbuf;
255             if (stat (mounted, &sbuf) == 0)
256                 chown (mounted_temp, sbuf.st_uid, sbuf.st_gid);
257         }
258
259         /* rename mtemp to mtab */
260         if (rename (mounted_temp, mounted) < 0) {
261                 int errsv = errno;
262                 fprintf(stderr, _("can't rename %s to %s: %s\n"),
263                         mounted_temp, mounted, strerror(errsv));
264         }
265
266         unlock_mtab();
267 }
268
269 int main(int argc, char **argv)
270 {
271         int i, stop = 100000;
272         FILE *fout = NULL;
273
274         if (argc > 1)
275                 stop = atoi(argv[1]);
276
277         for (i = 0; i < stop; i++) {
278                 update_mtab();
279         }
280
281         if (argc > 2)
282                 fout = fopen(argv[2],"a");
283         if (!fout)
284                 fout = stdout;
285         fprintf(fout, "completed %d iterations\n", stop);
286         return 0;
287 }