Whamcloud - gitweb
LU-10212 test: ESTALE read
[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.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2014, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  */
32
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <pthread.h>
40 #include <sys/file.h>
41 #include <sys/wait.h>
42 #include <stdarg.h>
43
44 #define MAX_PATH_LENGTH 4096
45 /**
46  * helper functions
47  */
48 int t_fcntl(int fd, int cmd, ...)
49 {
50         va_list ap;
51         long arg;
52         struct flock *lock;
53         int rc = -1;
54
55         va_start(ap, cmd);
56         switch (cmd) {
57         case F_GETFL:
58                 va_end(ap);
59                 rc = fcntl(fd, cmd);
60                 if (rc == -1) {
61                         rc = -errno;
62                         fprintf(stderr, "fcntl GETFL failed: %s\n",
63                                 strerror(errno));
64                         return rc;
65                 }
66                 break;
67         case F_SETFL:
68                 arg = va_arg(ap, long);
69                 va_end(ap);
70                 rc = fcntl(fd, cmd, arg);
71                 if (rc == -1) {
72                         rc = -errno;
73                         fprintf(stderr, "fcntl SETFL %ld failed: %s\n",
74                                 arg, strerror(errno));
75                         return rc ;
76                 }
77                 break;
78         case F_GETLK:
79         case F_SETLK:
80         case F_SETLKW:
81                 lock = va_arg(ap, struct flock *);
82                 va_end(ap);
83                 rc = fcntl(fd, cmd, lock);
84                 if (rc == -1) {
85                         rc = -errno;
86                         fprintf(stderr, "fcntl cmd %d failed: %s\n",
87                                 cmd, strerror(errno));
88                         return rc ;
89                 }
90                 break;
91         case F_DUPFD:
92                 arg = va_arg(ap, long);
93                 va_end(ap);
94                 rc = fcntl(fd, cmd, arg);
95                 if (rc == -1) {
96                         rc = -errno;
97                         fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n",
98                                 (int)arg, strerror(errno));
99                         return rc;
100                 }
101                 break;
102         default:
103                 va_end(ap);
104                 fprintf(stderr, "fcntl cmd %d not supported\n", cmd);
105                 return rc;
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[4]);
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 < 0) || (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[2]);
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[2]);
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 int t4(int argc, char *argv[])
361 {
362         struct flock lock = {
363                 .l_type = F_WRLCK,
364                 .l_whence = SEEK_SET,
365                 .l_start = 0,
366                 .l_len = 10,
367         };
368
369         int fd, fd2;
370         pid_t child_pid;
371         int child_status;
372         int rc = EXIT_SUCCESS;
373
374         if (argc != 4) {
375                 fprintf(stderr, "Usage: ./flocks_test 4 file1 file2\n");
376                 return EXIT_FAILURE;
377         }
378
379         if ((fd = open(argv[2], O_RDWR)) < 0) {
380                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
381                 return EXIT_FAILURE;
382         }
383         if ((fd2 = open(argv[3], O_RDWR)) < 0) {
384                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
385                 rc = EXIT_FAILURE;
386                 goto out;
387         }
388
389         child_pid = fork();
390         if (child_pid < 0) {
391                 perror("fork");
392                 rc = EXIT_FAILURE;
393                 goto out;
394         }
395
396         if (child_pid == 0) {
397                 printf("%d: get lock1\n", getpid());
398                 fflush(stdout);
399                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
400                         fprintf(stderr, "%d: cannot get lock1: %s\n",
401                                 getpid(), strerror(errno));
402                         rc = EXIT_FAILURE;
403                         goto out_child;
404                 }
405                 printf("%d: done\n", getpid());
406                 sleep(3);
407                 printf("%d: get lock2\n", getpid());
408                 fflush(stdout);
409                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
410                         fprintf(stderr, "%d: cannot get lock2: %s\n",
411                                 getpid(), strerror(errno));
412
413                         if (errno == EDEADLK)
414                                 rc = EXIT_SUCCESS;
415                         else
416                                 rc = EXIT_FAILURE;
417
418                         goto out_child;
419                 }
420                 printf("%d: done\n", getpid());
421 out_child:
422                 printf("%d: exit rc=%d\n", getpid(), rc);
423                 exit(rc);
424         } else {
425                 printf("%d: get lock2\n", getpid());
426                 fflush(stdout);
427                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
428                         fprintf(stderr, "%d: cannot get lock2: %s\n",
429                                 getpid(), strerror(errno));
430                         rc = EXIT_FAILURE;
431                         goto out;
432                 }
433                 printf("%d: done\n", getpid());
434                 sleep(3);
435                 printf("%d: get lock1\n", getpid());
436                 fflush(stdout);
437                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
438                         fprintf(stderr, "%d: cannot get lock1: %s\n",
439                                 getpid(), strerror(errno));
440
441                         if (errno != EDEADLK) {
442                                 rc = EXIT_FAILURE;
443                                 goto out;
444                         }
445                 }
446                 printf("%d: done\n", getpid());
447         }
448
449         sleep(1);
450
451         if (close(fd) < 0) {
452                 fprintf(stderr, "%d: error closing file1: %s\n",
453                         getpid(), strerror(errno));
454                 rc = EXIT_FAILURE;
455         }
456
457         if (close(fd2) < 0) {
458                 fprintf(stderr, "%d: error closing file2: %s\n",
459                         getpid(), strerror(errno));
460                 rc = EXIT_FAILURE;
461         }
462
463         if (waitpid(child_pid, &child_status, 0) < 0) {
464                 fprintf(stderr, "%d: cannot get termination status of %d: %s\n",
465                         getpid(), child_pid, strerror(errno));
466                 rc = EXIT_FAILURE;
467         } else if (!WIFEXITED(child_status)) {
468                 fprintf(stderr, "%d: child %d terminated with status %d\n",
469                         getpid(), child_pid, child_status);
470                 rc = EXIT_FAILURE;
471         } else {
472                 rc = WEXITSTATUS(child_status);
473         }
474
475 out:
476         printf("%d: exit rc=%d\n", getpid(), rc);
477         return rc;
478 }
479
480 #define T5_USAGE                                                              \
481         "Usage: ./flocks_test 5 set|get|unlock [read|write] [sleep N] file1\n"\
482 "       set: F_SETLKW F_WRLCK\n"                                              \
483 "       get: F_GETLK F_WRLCK  (conflict)\n"                                   \
484 "       unlock: F_SETLKW F_UNLCK\n"                                           \
485 "       read|write: lock mode, write by default\n"                            \
486 "       sleep N: sleep for N secs after fcntl\n"                              \
487 "       file1: fcntl is called for this file\n"
488
489 int t5(int argc, char *argv[])
490 {
491         struct flock lock = {
492                 .l_type = F_WRLCK,
493                 .l_whence = SEEK_SET,
494         };
495
496         int setlk = 0, getlk = 0, unlk = 0, secs = 0;
497         int pos;
498         int fd;
499         int rc = 0;
500
501         if (argc < 4 || argc > 7) {
502                 fprintf(stderr, T5_USAGE);
503                 return EXIT_FAILURE;
504         }
505
506         if (!strncmp(argv[2], "set", 4))
507                 setlk = 1;
508         else if (!strncmp(argv[2], "get", 4))
509                 getlk = 1;
510         else if (!strncmp(argv[2], "unlock", 7))
511                 unlk = 1;
512         else {
513                 fprintf(stderr, "Wrong 2nd argument: %s\n", argv[2]);
514                 return EXIT_FAILURE;
515         }
516
517         pos = 3;
518
519         if (!strncmp(argv[pos], "read", 5)) {
520                 lock.l_type = F_RDLCK;
521                 pos++;
522         } else if (!strncmp(argv[pos], "write", 6)) {
523                 lock.l_type = F_WRLCK;
524                 pos++;
525         }
526
527         if (!strncmp(argv[pos], "sleep", 6)) {
528                 secs = atoi(argv[pos + 1]);
529                 if (secs < 0 || secs > 10) {
530                         fprintf(stderr, "Sleep argument is wrong: %s\n",
531                                 argv[pos + 1]);
532                         return EXIT_FAILURE;
533                 }
534                 pos += 2;
535         }
536
537         fd = open(argv[pos], O_RDWR);
538         if (fd < 0) {
539                 fprintf(stderr, "Couldn't open file: %s\n", argv[pos]);
540                 return EXIT_FAILURE;
541         }
542
543         fprintf(stderr, "\nFLOCKS_TEST 5: %s %s flock\n",
544                 setlk ? "SET" : getlk ? "GET" : "UNLOCK",
545                 lock.l_type == F_WRLCK ? "write" : "read");
546
547         if (setlk) {
548                 rc = t_fcntl(fd, F_SETLKW, &lock);
549         } else if (getlk) {
550                 rc = t_fcntl(fd, F_GETLK, &lock);
551         } else if (unlk) {
552                 lock.l_type = F_UNLCK;
553                 rc = t_fcntl(fd, F_SETLKW, &lock);
554         }
555
556         if (secs)
557                 sleep(secs);
558
559         close(fd);
560         return rc < 0 ? -rc : 0;
561
562 }
563
564 /** ==============================================================
565  * program entry
566  */
567 void usage(void)
568 {
569         fprintf(stderr, "usage: ./flocks_test test# [corresponding arguments]\n");
570 }
571
572 int main(int argc, char* argv[])
573 {
574         int test_no;
575         int rc = EXIT_SUCCESS;
576
577         if (argc < 1) {
578                 usage();
579                 exit(EXIT_FAILURE);
580         }
581         test_no = atoi(argv[1]);
582
583         switch(test_no) {
584         case 1:
585                 rc = t1(argc, argv);
586                 break;
587         case 2:
588                 rc = t2(argc, argv);
589                 break;
590         case 3:
591                 rc = t3(argc, argv);
592                 break;
593         case 4:
594                 rc = t4(argc, argv);
595                 break;
596         case 5:
597                 rc = t5(argc, argv);
598                 break;
599         default:
600                 fprintf(stderr, "unknow test number %s\n", argv[1]);
601                 break;
602         }
603         return rc;
604 }