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