Whamcloud - gitweb
d738f1f07ca5ce4b904e103a7bc97213da315cf2
[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         int rc = 0;
136
137         if (argc != 5) {
138                 t1_usage();
139                 return EXIT_FAILURE;
140         }
141
142         if (!strncmp(argv[2], "on", 3)) {
143                 mount_with_flock = 1;
144         } else if (!strncmp(argv[2], "off", 4)) {
145                 mount_with_flock = 0;
146         } else {
147                 t1_usage();
148                 return EXIT_FAILURE;
149         }
150
151         if ((fd = open(argv[4], O_RDWR)) < 0) {
152                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
153                 return EXIT_FAILURE;
154         }
155
156         if (!strncmp(argv[3], "-c", 3)) {
157                 struct flock fl;
158
159                 fl.l_type = F_RDLCK;
160                 fl.l_whence = SEEK_SET;
161                 fl.l_start = 0;
162                 fl.l_len = 1;
163
164                 error = fcntl(fd, F_SETLK, &fl);
165         } else if (!strncmp(argv[3], "-l", 3)) {
166                 error = lockf(fd, F_LOCK, 1);
167         } else if (!strncmp(argv[3], "-f", 3)) {
168                 error = flock(fd, LOCK_EX);
169         } else {
170                 t1_usage();
171                 rc = EXIT_FAILURE;
172                 goto out;
173         }
174
175         if (mount_with_flock)
176                 rc = ((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
177         else
178                 rc = ((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
179
180 out:
181         if (fd >= 0)
182                 close(fd);
183         return rc;
184 }
185
186 /** ===============================================================
187  * test number 2
188  * 
189  * 2 threads flock ops interweave
190  */
191 typedef struct {
192         struct flock* lock;
193         int fd;
194 } th_data;
195
196 void* t2_thread1(void *arg)
197 {
198         struct flock *lock = ((th_data *)arg)->lock;
199         int fd             = ((th_data *)arg)->fd;
200
201         printf("thread 1: set write lock (blocking)\n");
202         lock->l_type = F_WRLCK;
203         t_fcntl(fd, F_SETLKW, lock);
204         printf("thread 1: set write lock done\n");
205         t_fcntl(fd, F_GETLK, lock);
206         printf("thread 1: unlock\n");
207         lock->l_type = F_UNLCK;
208         t_fcntl(fd, F_SETLK, lock);
209         printf("thread 1: unlock done\n");
210         return 0;
211 }
212
213 void* t2_thread2(void *arg)
214 {
215         struct flock *lock = ((th_data *)arg)->lock;
216         int fd             = ((th_data *)arg)->fd;
217
218         sleep(2);
219         printf("thread 2: unlock\n");
220         lock->l_type = F_UNLCK;
221         t_fcntl(fd, F_SETLK, lock);
222         printf("thread 2: unlock done\n");
223         printf("thread 2: set write lock (non-blocking)\n");
224         lock->l_type = F_WRLCK;
225         t_fcntl(fd, F_SETLK, lock);
226         printf("thread 2: set write lock done\n");
227         t_fcntl(fd, F_GETLK, lock);
228         return 0;
229 }
230
231 int t2(int argc, char* argv[])
232 {
233         struct flock lock = {
234                 .l_type = F_RDLCK,
235                 .l_whence = SEEK_SET,
236         };
237         char file[MAX_PATH_LENGTH] = "";
238         int  fd, rc;
239         pthread_t th1, th2;
240         th_data   ta;
241
242         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
243
244         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
245         if (fd < 0) {
246                 fprintf(stderr, "error open file: %s\n", file);
247                 return EXIT_FAILURE;
248         }
249
250         t_fcntl(fd, F_SETFL, O_APPEND);
251         rc = t_fcntl(fd, F_GETFL);
252         if ((rc & O_APPEND) == 0) {
253                 fprintf(stderr, "error get flag: ret %x\n", rc);
254                 rc = EXIT_FAILURE;
255                 goto out;
256         }
257
258         ta.lock = &lock;
259         ta.fd   = fd;
260         rc = pthread_create(&th1, NULL, t2_thread1, &ta);
261         if (rc) {
262                 fprintf(stderr, "error create thread 1\n");
263                 rc = EXIT_FAILURE;
264                 goto out;
265         }
266         rc = pthread_create(&th2, NULL, t2_thread2, &ta);
267         if (rc) {
268                 fprintf(stderr, "error create thread 2\n");
269                 rc = EXIT_FAILURE;
270                 goto out;
271         }
272         (void)pthread_join(th1, NULL);
273         (void)pthread_join(th2, NULL);
274 out:
275         t_unlink(file);
276         close(fd);
277         return rc;
278 }
279
280 /** =================================================================
281  * test number 3
282  *
283  * Bug 24040: Two conflicting flocks from same process different fds should fail
284  *            two conflicting flocks from different processes but same fs
285  *            should succeed.
286  */
287 int t3(int argc, char *argv[])
288 {
289         int fd, fd2;
290         int pid;
291         int rc = EXIT_SUCCESS;
292
293         if (argc != 3) {
294                 fprintf(stderr, "Usage: ./flocks_test 3 filename\n");
295                 return EXIT_FAILURE;
296         }
297
298         if ((fd = open(argv[2], O_RDWR)) < 0) {
299                 fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
300                 return EXIT_FAILURE;
301         }
302         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
303                 perror("first flock failed");
304                 rc = EXIT_FAILURE;
305                 goto out;
306         }
307         if ((fd2 = open(argv[2], O_RDWR)) < 0) {
308                 fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
309                 rc = EXIT_FAILURE;
310                 goto out;
311         }
312         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
313                 fprintf(stderr, "Second flock succeeded - FAIL\n");
314                 rc = EXIT_FAILURE;
315                 close(fd2);
316                 goto out;
317         }
318
319         close(fd2);
320
321         pid = fork();
322         if (pid == -1) {
323                 perror("fork");
324                 rc = EXIT_FAILURE;
325                 goto out;
326         }
327
328         if (pid == 0) {
329                 if ((fd2 = open(argv[2], O_RDWR)) < 0) {
330                         fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
331                         rc = EXIT_FAILURE;
332                         exit(rc);
333                 }
334                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
335                         fprintf(stderr, "Second flock succeeded - FAIL\n");
336                         rc = EXIT_FAILURE;
337                         goto out_child;
338                 }
339                 if (flock(fd, LOCK_UN) == -1) {
340                         fprintf(stderr, "Child unlock on parent fd failed\n");
341                         rc = EXIT_FAILURE;
342                         goto out_child;
343                 }
344                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
345                         fprintf(stderr, "Relock after parent unlock failed!\n");
346                         rc = EXIT_FAILURE;
347                         goto out_child;
348                 }
349         out_child:
350                 close(fd2);
351                 exit(rc);
352         }
353
354         waitpid(pid, &rc, 0);
355 out:
356         close(fd);
357         return rc;
358 }
359
360
361 /** ==============================================================
362  * program entry
363  */
364 void usage(void)
365 {
366         fprintf(stderr, "usage: ./flocks_test test# [corresponding arguments]\n");
367 }
368
369 int main(int argc, char* argv[])
370 {
371         int test_no;
372         int rc = EXIT_SUCCESS;
373
374         if (argc < 1) {
375                 usage();
376                 exit(EXIT_FAILURE);
377         }
378         test_no = atoi(argv[1]);
379
380         switch(test_no) {
381         case 1:
382                 rc = t1(argc, argv);
383                 break;
384         case 2:
385                 rc = t2(argc, argv);
386                 break;
387         case 3:
388                 rc = t3(argc, argv);
389                 break;
390         default:
391                 fprintf(stderr, "unknow test number %s\n", argv[1]);
392                 break;
393         }
394         return rc;
395 }