Whamcloud - gitweb
LU-1347 build: remove the vim/emacs modelines
[fs/lustre-release.git] / lustre / tests / flocks_test.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2011, Whamcloud, Inc.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <pthread.h>
44 #include <sys/file.h>
45 #include <sys/wait.h>
46 #include <stdarg.h>
47
48 #define MAX_PATH_LENGTH 4096
49 /**
50  * helper functions
51  */
52 int t_fcntl(int fd, int cmd, ...)
53 {
54         va_list ap;
55         long arg;
56         struct flock *lock;
57         int rc = -1;
58
59         va_start(ap, cmd);
60         switch (cmd) {
61         case F_GETFL:
62                 va_end(ap);
63                 rc = fcntl(fd, cmd);
64                 if (rc == -1) {
65                         fprintf(stderr, "fcntl GETFL failed: %s\n",
66                                 strerror(errno));
67                         return(1);
68                 }
69                 break;
70         case F_SETFL:
71                 arg = va_arg(ap, long);
72                 va_end(ap);
73                 rc = fcntl(fd, cmd, arg);
74                 if (rc == -1) {
75                         fprintf(stderr, "fcntl SETFL %ld failed: %s\n",
76                                 arg, strerror(errno));
77                         return(1);
78                 }
79                 break;
80         case F_GETLK:
81         case F_SETLK:
82         case F_SETLKW:
83                 lock = va_arg(ap, struct flock *);
84                 va_end(ap);
85                 rc = fcntl(fd, cmd, lock);
86                 if (rc == -1) {
87                         fprintf(stderr, "fcntl cmd %d failed: %s\n",
88                                 cmd, strerror(errno));
89                         return(1);
90                 }
91                 break;
92         case F_DUPFD:
93                 arg = va_arg(ap, long);
94                 va_end(ap);
95                 rc = fcntl(fd, cmd, arg);
96                 if (rc == -1) {
97                         fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n",
98                                 (int)arg, strerror(errno));
99                         return(1);
100                 }
101                 break;
102         default:
103                 va_end(ap);
104                 fprintf(stderr, "fcntl cmd %d not supported\n", cmd);
105                 return(1);
106         }
107         return rc;
108 }
109
110 int t_unlink(const char *path)
111 {
112         int rc;
113
114         rc = unlink(path);
115         if (rc)
116                 fprintf(stderr, "unlink(%s) error: %s\n", path, strerror(errno));
117         return rc;
118 }
119
120 /** =================================================================
121  * test number 1
122  * 
123  * normal flock test
124  */
125 void t1_usage(void)
126 {
127         fprintf(stderr, "usage: ./flocks_test 1 on|off -c|-f|-l /path/to/file\n");
128 }
129
130 int t1(int argc, char *argv[])
131 {
132         int fd;
133         int mount_with_flock = 0;
134         int error = 0;
135
136         if (argc != 5) {
137                 t1_usage();
138                 return EXIT_FAILURE;
139         }
140
141         if (!strncmp(argv[2], "on", 3)) {
142                 mount_with_flock = 1;
143         } else if (!strncmp(argv[2], "off", 4)) {
144                 mount_with_flock = 0;
145         } else {
146                 t1_usage();
147                 return EXIT_FAILURE;
148         }
149
150         if ((fd = open(argv[4], O_RDWR)) < 0) {
151                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
152                 return EXIT_FAILURE;
153         }
154
155         if (!strncmp(argv[3], "-c", 3)) {
156                 struct flock fl;
157
158                 fl.l_type = F_RDLCK;
159                 fl.l_whence = SEEK_SET;
160                 fl.l_start = 0;
161                 fl.l_len = 1;
162
163                 error = fcntl(fd, F_SETLK, &fl);
164         } else if (!strncmp(argv[3], "-l", 3)) {
165                 error = lockf(fd, F_LOCK, 1);
166         } else if (!strncmp(argv[3], "-f", 3)) {
167                 error = flock(fd, LOCK_EX);
168         } else {
169                 t1_usage();
170                 return EXIT_FAILURE;
171         }
172
173         if (mount_with_flock)
174                 return((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
175         else
176                 return((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
177 }
178
179 /** ===============================================================
180  * test number 2
181  * 
182  * 2 threads flock ops interweave
183  */
184 typedef struct {
185         struct flock* lock;
186         int fd;
187 } th_data;
188
189 void* t2_thread1(void *arg)
190 {
191         struct flock *lock = ((th_data *)arg)->lock;
192         int fd             = ((th_data *)arg)->fd;
193
194         printf("thread 1: set write lock (blocking)\n");
195         lock->l_type = F_WRLCK;
196         t_fcntl(fd, F_SETLKW, lock);
197         printf("thread 1: set write lock done\n");
198         t_fcntl(fd, F_GETLK, lock);
199         printf("thread 1: unlock\n");
200         lock->l_type = F_UNLCK;
201         t_fcntl(fd, F_SETLK, lock);
202         printf("thread 1: unlock done\n");
203         return 0;
204 }
205
206 void* t2_thread2(void *arg)
207 {
208         struct flock *lock = ((th_data *)arg)->lock;
209         int fd             = ((th_data *)arg)->fd;
210
211         sleep(2);
212         printf("thread 2: unlock\n");
213         lock->l_type = F_UNLCK;
214         t_fcntl(fd, F_SETLK, lock);
215         printf("thread 2: unlock done\n");
216         printf("thread 2: set write lock (non-blocking)\n");
217         lock->l_type = F_WRLCK;
218         t_fcntl(fd, F_SETLK, lock);
219         printf("thread 2: set write lock done\n");
220         t_fcntl(fd, F_GETLK, lock);
221         return 0;
222 }
223
224 int t2(int argc, char* argv[])
225 {
226         struct flock lock = {
227                 .l_type = F_RDLCK,
228                 .l_whence = SEEK_SET,
229         };
230         char file[MAX_PATH_LENGTH] = "";
231         int  fd, rc;
232         pthread_t th1, th2;
233         th_data   ta;
234
235         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
236
237         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
238         if (fd < 0) {
239                 fprintf(stderr, "error open file: %s\n", file);
240                 return EXIT_FAILURE;
241         }
242
243         t_fcntl(fd, F_SETFL, O_APPEND);
244         rc = t_fcntl(fd, F_GETFL);
245         if ((rc & O_APPEND) == 0) {
246                 fprintf(stderr, "error get flag: ret %x\n", rc);
247                 return EXIT_FAILURE;
248         }
249
250         ta.lock = &lock;
251         ta.fd   = fd;
252         rc = pthread_create(&th1, NULL, t2_thread1, &ta);
253         if (rc) {
254                 fprintf(stderr, "error create thread 1\n");
255                 rc = EXIT_FAILURE;
256                 goto out;
257         }
258         rc = pthread_create(&th2, NULL, t2_thread2, &ta);
259         if (rc) {
260                 fprintf(stderr, "error create thread 2\n");
261                 rc = EXIT_FAILURE;
262                 goto out;
263         }
264         (void)pthread_join(th1, NULL);
265         (void)pthread_join(th2, NULL);
266 out:
267         t_unlink(file);
268         close(fd);
269         return rc;
270 }
271
272 /** =================================================================
273  * test number 3
274  *
275  * Bug 24040: Two conflicting flocks from same process different fds should fail
276  *            two conflicting flocks from different processes but same fs
277  *            should succeed.
278  */
279 int t3(int argc, char *argv[])
280 {
281         int fd, fd2;
282         int pid;
283         int rc = EXIT_SUCCESS;
284
285         if (argc != 3) {
286                 fprintf(stderr, "Usage: ./flocks_test 3 filename\n");
287                 return EXIT_FAILURE;
288         }
289
290         if ((fd = open(argv[2], O_RDWR)) < 0) {
291                 fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
292                 return EXIT_FAILURE;
293         }
294         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
295                 perror("first flock failed");
296                 rc = EXIT_FAILURE;
297                 goto out;
298         }
299         if ((fd2 = open(argv[2], O_RDWR)) < 0) {
300                 fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
301                 rc = EXIT_FAILURE;
302                 goto out;
303         }
304         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
305                 fprintf(stderr, "Second flock succeeded - FAIL\n");
306                 rc = EXIT_FAILURE;
307                 close(fd2);
308                 goto out;
309         }
310
311         close(fd2);
312
313         pid = fork();
314         if (pid == -1) {
315                 perror("fork");
316                 rc = EXIT_FAILURE;
317                 goto out;
318         }
319
320         if (pid == 0) {
321                 if ((fd2 = open(argv[2], O_RDWR)) < 0) {
322                         fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
323                         rc = EXIT_FAILURE;
324                         exit(rc);
325                 }
326                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
327                         fprintf(stderr, "Second flock succeeded - FAIL\n");
328                         rc = EXIT_FAILURE;
329                         goto out_child;
330                 }
331                 if (flock(fd, LOCK_UN) == -1) {
332                         fprintf(stderr, "Child unlock on parent fd failed\n");
333                         rc = EXIT_FAILURE;
334                         goto out_child;
335                 }
336                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
337                         fprintf(stderr, "Relock after parent unlock failed!\n");
338                         rc = EXIT_FAILURE;
339                         goto out_child;
340                 }
341         out_child:
342                 close(fd2);
343                 exit(rc);
344         }
345
346         waitpid(pid, &rc, 0);
347 out:
348         close(fd);
349         return rc;
350 }
351
352
353 /** ==============================================================
354  * program entry
355  */
356 void usage(void)
357 {
358         fprintf(stderr, "usage: ./flocks_test test# [corresponding arguments]\n");
359 }
360
361 int main(int argc, char* argv[])
362 {
363         int test_no;
364         int rc = EXIT_SUCCESS;
365
366         if (argc < 1) {
367                 usage();
368                 exit(EXIT_FAILURE);
369         }
370         test_no = atoi(argv[1]);
371
372         switch(test_no) {
373         case 1:
374                 rc = t1(argc, argv);
375                 break;
376         case 2:
377                 rc = t2(argc, argv);
378                 break;
379         case 3:
380                 rc = t3(argc, argv);
381                 break;
382         default:
383                 fprintf(stderr, "unknow test number %s\n", argv[1]);
384                 break;
385         }
386         return rc;
387 }