Whamcloud - gitweb
LU-17452 tests: fix interop sanityn tests with b2_15
[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  */
31
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <pthread.h>
39 #include <sys/file.h>
40 #include <sys/wait.h>
41 #include <sys/time.h>
42 #include <stdarg.h>
43 #include <ctype.h>
44
45 #define MAX_PATH_LENGTH 4096
46
47
48 static double now(void)
49 {
50         struct timeval tv;
51
52         gettimeofday(&tv, NULL);
53         return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
54 }
55
56 /**
57  * helper functions
58  */
59 static int t_fcntl(int fd, int cmd, ...)
60 {
61         va_list ap;
62         long arg;
63         struct flock *lock;
64         int rc = -1;
65
66         va_start(ap, cmd);
67         switch (cmd) {
68         case F_GETFL:
69                 va_end(ap);
70                 rc = fcntl(fd, cmd);
71                 if (rc == -1) {
72                         rc = -errno;
73                         fprintf(stderr, "fcntl GETFL failed: %s\n",
74                                 strerror(errno));
75                         return rc;
76                 }
77                 break;
78         case F_SETFL:
79                 arg = va_arg(ap, long);
80                 va_end(ap);
81                 rc = fcntl(fd, cmd, arg);
82                 if (rc == -1) {
83                         rc = -errno;
84                         fprintf(stderr, "fcntl SETFL %ld failed: %s\n",
85                                 arg, strerror(errno));
86                         return rc;
87                 }
88                 break;
89         case F_GETLK:
90         case F_SETLK:
91         case F_SETLKW:
92                 lock = va_arg(ap, struct flock *);
93                 va_end(ap);
94                 rc = fcntl(fd, cmd, lock);
95                 if (rc == -1) {
96                         rc = -errno;
97                         fprintf(stderr, "fcntl cmd %d failed: %s\n",
98                                 cmd, strerror(errno));
99                         return rc;
100                 }
101                 break;
102         case F_DUPFD:
103                 arg = va_arg(ap, long);
104                 va_end(ap);
105                 rc = fcntl(fd, cmd, arg);
106                 if (rc == -1) {
107                         rc = -errno;
108                         fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n",
109                                 (int)arg, strerror(errno));
110                         return rc;
111                 }
112                 break;
113         default:
114                 va_end(ap);
115                 fprintf(stderr, "fcntl cmd %d not supported\n", cmd);
116                 return rc;
117         }
118         return rc;
119 }
120
121 static int t_unlink(const char *path)
122 {
123         int rc;
124
125         rc = unlink(path);
126         if (rc)
127                 fprintf(stderr,
128                         "unlink(%s) error: %s\n", path, strerror(errno));
129         return rc;
130 }
131
132 /** =================================================================
133  * test number 1
134  *
135  * normal flock test
136  */
137 static void t1_usage(void)
138 {
139         fprintf(stderr,
140                 "usage: flocks_test 1 {on|off} {-c|-f|-l} /path/to/file\n");
141 }
142
143 static int t1(int argc, char *argv[])
144 {
145         int fd;
146         int mount_with_flock = 0;
147         int error = 0;
148         int rc = 0;
149
150         if (argc != 5) {
151                 t1_usage();
152                 return EXIT_FAILURE;
153         }
154
155         if (!strncmp(argv[2], "on", 3)) {
156                 mount_with_flock = 1;
157         } else if (!strncmp(argv[2], "off", 4)) {
158                 mount_with_flock = 0;
159         } else {
160                 t1_usage();
161                 return EXIT_FAILURE;
162         }
163
164         fd = open(argv[4], O_RDWR);
165         if (fd < 0) {
166                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[4],
167                         strerror(errno));
168                 return EXIT_FAILURE;
169         }
170
171         if (!strncmp(argv[3], "-c", 3)) {
172                 struct flock fl;
173
174                 fl.l_type = F_RDLCK;
175                 fl.l_whence = SEEK_SET;
176                 fl.l_start = 0;
177                 fl.l_len = 1;
178
179                 error = fcntl(fd, F_SETLK, &fl);
180         } else if (!strncmp(argv[3], "-l", 3)) {
181                 error = lockf(fd, F_LOCK, 1);
182         } else if (!strncmp(argv[3], "-f", 3)) {
183                 error = flock(fd, LOCK_EX);
184         } else {
185                 t1_usage();
186                 rc = EXIT_FAILURE;
187                 goto out;
188         }
189
190         if (mount_with_flock)
191                 rc = ((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
192         else
193                 rc = ((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
194
195 out:
196         if (fd >= 0)
197                 close(fd);
198         return rc;
199 }
200
201 /** ===============================================================
202  * test number 2
203  *
204  * 2 threads flock ops interweave
205  */
206 struct thread_info {
207         struct flock *lock;
208         int fd;
209         int rc;
210 } th_data;
211
212 static void *t2_thread1(void *arg)
213 {
214         struct thread_info *ti = arg;
215         struct flock *lock = ti->lock;
216         int fd = ti->fd;
217
218         printf("thread 1: set write lock (blocking): rc = %d\n", ti->rc);
219         lock->l_type = F_WRLCK;
220         t_fcntl(fd, F_SETLKW, lock);
221         printf("thread 1: set write lock done: rc = %d\n", ti->rc);
222         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
223         printf("thread 1: unlock: rc = %d\n", ti->rc);
224         lock->l_type = F_UNLCK;
225         ti->rc += t_fcntl(fd, F_SETLK, lock);
226         printf("thread 1: unlock done: rc = %d\n", ti->rc);
227
228         if (ti->rc)
229                 fprintf(stdout, "thread1 exiting with rc = %d\n", ti->rc);
230         return &ti->rc;
231 }
232
233 static void *t2_thread2(void *arg)
234 {
235         struct thread_info *ti = arg;
236         struct flock *lock = ti->lock;
237         int fd = ti->fd;
238
239         sleep(2);
240         printf("thread 2: unlock: rc = %d\n", ti->rc);
241         lock->l_type = F_UNLCK;
242         ti->rc += t_fcntl(fd, F_SETLK, lock);
243         printf("thread 2: unlock done: rc = %d\n", ti->rc);
244         printf("thread 2: set write lock (non-blocking): rc = %d\n", ti->rc);
245         lock->l_type = F_WRLCK;
246         ti->rc += t_fcntl(fd, F_SETLK, lock);
247         printf("thread 2: set write lock done: rc = %d\n", ti->rc);
248         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
249
250         if (ti->rc)
251                 fprintf(stdout, "thread2 exiting with rc = %d\n", ti->rc);
252         return &ti->rc;
253 }
254
255 static int t2(int argc, char *argv[])
256 {
257         struct flock lock = {
258                 .l_type = F_RDLCK,
259                 .l_whence = SEEK_SET,
260         };
261         char file[MAX_PATH_LENGTH] = "";
262         int  fd, rc;
263         pthread_t th1, th2;
264         struct thread_info ti;
265
266         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
267
268         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
269         if (fd < 0) {
270                 fprintf(stderr, "error open file '%s': %s\n", file,
271                         strerror(errno));
272                 return EXIT_FAILURE;
273         }
274
275         t_fcntl(fd, F_SETFL, O_APPEND);
276         rc = t_fcntl(fd, F_GETFL);
277         if ((rc < 0) || (rc & O_APPEND) == 0) {
278                 fprintf(stderr, "error get flag: ret %x\n", rc);
279                 rc = EXIT_FAILURE;
280                 goto out;
281         }
282
283         ti.lock = &lock;
284         ti.fd   = fd;
285         ti.rc   = 0;
286         rc = pthread_create(&th1, NULL, t2_thread1, &ti);
287         if (rc) {
288                 fprintf(stderr, "error create thread 1\n");
289                 rc = EXIT_FAILURE;
290                 goto out;
291         }
292         rc = pthread_create(&th2, NULL, t2_thread2, &ti);
293         if (rc) {
294                 fprintf(stderr, "error create thread 2\n");
295                 rc = EXIT_FAILURE;
296                 goto out;
297         }
298         pthread_join(th1, NULL);
299         pthread_join(th2, NULL);
300         if (ti.rc)
301                 rc = EXIT_FAILURE;
302 out:
303         t_unlink(file);
304         close(fd);
305
306         return rc;
307 }
308
309 /** =================================================================
310  * test number 3
311  *
312  * Bug 24040: Two conflicting flocks from same process different fds should fail
313  *            two conflicting flocks from different processes but same fs
314  *            should succeed.
315  */
316 static int t3(int argc, char *argv[])
317 {
318         int fd, fd2;
319         int pid;
320         int rc = EXIT_SUCCESS;
321
322         if (argc != 3) {
323                 fprintf(stderr, "usage: flocks_test 3 filename\n");
324                 return EXIT_FAILURE;
325         }
326
327         fd = open(argv[2], O_RDWR);
328         if (fd < 0) {
329                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
330                         strerror(errno));
331                 return EXIT_FAILURE;
332         }
333         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
334                 perror("first flock failed");
335                 rc = EXIT_FAILURE;
336                 goto out;
337         }
338         fd2 = open(argv[2], O_RDWR);
339         if (fd2 < 0) {
340                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
341                         strerror(errno));
342                 rc = EXIT_FAILURE;
343                 goto out;
344         }
345         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
346                 fprintf(stderr, "Second flock succeeded - FAIL\n");
347                 rc = EXIT_FAILURE;
348                 close(fd2);
349                 goto out;
350         }
351
352         close(fd2);
353
354         pid = fork();
355         if (pid == -1) {
356                 perror("fork");
357                 rc = EXIT_FAILURE;
358                 goto out;
359         }
360
361         if (pid == 0) {
362                 fd2 = open(argv[2], O_RDWR);
363                 if (fd2 < 0) {
364                         fprintf(stderr, "Couldn't open file '%s': %s\n",
365                                 argv[1], strerror(errno));
366                         rc = EXIT_FAILURE;
367                         goto out;
368                 }
369                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
370                         fprintf(stderr, "Second flock succeeded - FAIL\n");
371                         rc = EXIT_FAILURE;
372                         goto out_child;
373                 }
374                 if (flock(fd, LOCK_UN) == -1) {
375                         fprintf(stderr, "Child unlock on parent fd failed\n");
376                         rc = EXIT_FAILURE;
377                         goto out_child;
378                 }
379                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
380                         fprintf(stderr, "Relock after parent unlock failed!\n");
381                         rc = EXIT_FAILURE;
382                         goto out_child;
383                 }
384         out_child:
385                 close(fd2);
386                 exit(rc);
387         }
388
389         waitpid(pid, &rc, 0);
390 out:
391         close(fd);
392         return rc;
393 }
394
395 static int t4(int argc, char *argv[])
396 {
397         struct flock lock = {
398                 .l_type = F_WRLCK,
399                 .l_whence = SEEK_SET,
400                 .l_start = 0,
401                 .l_len = 10,
402         };
403
404         int fd, fd2;
405         pid_t child_pid;
406         int child_status;
407         int rc = EXIT_SUCCESS;
408
409         if (argc != 4) {
410                 fprintf(stderr, "usage: flocks_test 4 file1 file2\n");
411                 return EXIT_FAILURE;
412         }
413
414         fd = open(argv[2], O_RDWR);
415         if (fd < 0) {
416                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
417                 return EXIT_FAILURE;
418         }
419         fd2 = open(argv[3], O_RDWR);
420         if (fd2 < 0) {
421                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
422                 rc = EXIT_FAILURE;
423                 goto out;
424         }
425
426         child_pid = fork();
427         if (child_pid < 0) {
428                 perror("fork");
429                 rc = EXIT_FAILURE;
430                 goto out;
431         }
432
433         if (child_pid == 0) {
434                 printf("%d: get lock1\n", getpid());
435                 fflush(stdout);
436                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
437                         fprintf(stderr, "%d: cannot get lock1: %s\n",
438                                 getpid(), strerror(errno));
439                         rc = EXIT_FAILURE;
440                         goto out_child;
441                 }
442                 printf("%d: done\n", getpid());
443                 sleep(3);
444                 printf("%d: get lock2\n", getpid());
445                 fflush(stdout);
446                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
447                         fprintf(stderr, "%d: cannot get lock2: %s\n",
448                                 getpid(), strerror(errno));
449
450                         if (errno == EDEADLK)
451                                 rc = EXIT_SUCCESS;
452                         else
453                                 rc = EXIT_FAILURE;
454
455                         goto out_child;
456                 }
457                 printf("%d: done\n", getpid());
458 out_child:
459                 printf("%d: exit rc=%d\n", getpid(), rc);
460                 exit(rc);
461         } else {
462                 printf("%d: get lock2\n", getpid());
463                 fflush(stdout);
464                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
465                         fprintf(stderr, "%d: cannot get lock2: %s\n",
466                                 getpid(), strerror(errno));
467                         rc = EXIT_FAILURE;
468                         goto out;
469                 }
470                 printf("%d: done\n", getpid());
471                 sleep(3);
472                 printf("%d: get lock1\n", getpid());
473                 fflush(stdout);
474                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
475                         fprintf(stderr, "%d: cannot get lock1: %s\n",
476                                 getpid(), strerror(errno));
477
478                         if (errno != EDEADLK) {
479                                 rc = EXIT_FAILURE;
480                                 goto out;
481                         }
482                 }
483                 printf("%d: done\n", getpid());
484         }
485
486         sleep(1);
487
488         if (close(fd) < 0) {
489                 fprintf(stderr, "%d: error closing file1: %s\n",
490                         getpid(), strerror(errno));
491                 rc = EXIT_FAILURE;
492         }
493
494         if (close(fd2) < 0) {
495                 fprintf(stderr, "%d: error closing file2: %s\n",
496                         getpid(), strerror(errno));
497                 rc = EXIT_FAILURE;
498         }
499
500         if (waitpid(child_pid, &child_status, 0) < 0) {
501                 fprintf(stderr, "%d: cannot get termination status of %d: %s\n",
502                         getpid(), child_pid, strerror(errno));
503                 rc = EXIT_FAILURE;
504         } else if (!WIFEXITED(child_status)) {
505                 fprintf(stderr, "%d: child %d terminated with status %d\n",
506                         getpid(), child_pid, child_status);
507                 rc = EXIT_FAILURE;
508         } else {
509                 rc = WEXITSTATUS(child_status);
510         }
511
512 out:
513         printf("%d: exit rc=%d\n", getpid(), rc);
514         return rc;
515 }
516
517 #define T5_USAGE                                                              \
518 "usage: flocks_test 5 {set|get|unlock} [read|write] [sleep N] file1\n"        \
519 "       set: F_SETLKW F_WRLCK\n"                                              \
520 "       get: F_GETLK F_WRLCK  (conflict)\n"                                   \
521 "       unlock: F_SETLKW F_UNLCK\n"                                           \
522 "       read|write: lock mode, write by default\n"                            \
523 "       sleep N: sleep for N secs after fcntl\n"                              \
524 "       file1: fcntl is called for this file\n"
525
526 static int t5(int argc, char *argv[])
527 {
528         struct flock lock = {
529                 .l_type = F_WRLCK,
530                 .l_whence = SEEK_SET,
531         };
532
533         int setlk = 0, getlk = 0, unlk = 0, secs = 0;
534         int pos;
535         int fd;
536         int rc = 0;
537
538         if (argc < 4 || argc > 7) {
539                 fprintf(stderr, T5_USAGE);
540                 return EXIT_FAILURE;
541         }
542
543         if (!strncmp(argv[2], "set", 4))
544                 setlk = 1;
545         else if (!strncmp(argv[2], "get", 4))
546                 getlk = 1;
547         else if (!strncmp(argv[2], "unlock", 7))
548                 unlk = 1;
549         else {
550                 fprintf(stderr, "Wrong 2nd argument: %s\n", argv[2]);
551                 return EXIT_FAILURE;
552         }
553
554         pos = 3;
555
556         if (!strncmp(argv[pos], "read", 5)) {
557                 lock.l_type = F_RDLCK;
558                 pos++;
559         } else if (!strncmp(argv[pos], "write", 6)) {
560                 lock.l_type = F_WRLCK;
561                 pos++;
562         }
563
564         if (!strncmp(argv[pos], "sleep", 6)) {
565                 secs = atoi(argv[pos + 1]);
566                 if (secs < 0 || secs > 10) {
567                         fprintf(stderr, "Sleep argument is wrong: %s\n",
568                                 argv[pos + 1]);
569                         return EXIT_FAILURE;
570                 }
571                 pos += 2;
572         }
573
574         fd = open(argv[pos], O_RDWR);
575         if (fd < 0) {
576                 fprintf(stderr, "Couldn't open file: %s\n", argv[pos]);
577                 return EXIT_FAILURE;
578         }
579
580         fprintf(stderr, "\nFLOCKS_TEST 5: %s %s flock\n",
581                 setlk ? "SET" : getlk ? "GET" : "UNLOCK",
582                 lock.l_type == F_WRLCK ? "write" : "read");
583
584         if (setlk) {
585                 rc = t_fcntl(fd, F_SETLKW, &lock);
586         } else if (getlk) {
587                 rc = t_fcntl(fd, F_GETLK, &lock);
588         } else if (unlk) {
589                 lock.l_type = F_UNLCK;
590                 rc = t_fcntl(fd, F_SETLKW, &lock);
591         }
592
593         if (secs)
594                 sleep(secs);
595
596         close(fd);
597         return rc < 0 ? -rc : 0;
598
599 }
600
601 #define T6BUF_SIZE      200
602
603 int set_lock(struct flock *lock, char *buf)
604 {
605         int i, v;
606         struct tag_node {
607                 char    tag;
608                 int     mode;
609         } tags[] = {
610                 { 'W', F_WRLCK },
611                 { 'R', F_RDLCK },
612                 { 'U', F_UNLCK },
613                 { 0, 0 }
614         };
615
616         for (i = 0; isspace(buf[i]) && i < T6BUF_SIZE;)
617                 i++;
618         for (v = 0; tags[v].tag && i < T6BUF_SIZE; v++) {
619                 if (buf[i] == tags[v].tag) {
620                         char *head;
621
622                         head = buf + i + 1;
623                         for (; buf[i] != ','; i++)
624                                 if (i >= T6BUF_SIZE)
625                                         break;
626                         buf[i] = '\0';
627                         lock->l_start = atol(head);
628                         if (lock->l_start < 0)
629                                 break;
630                         for (; !isdigit(buf[i]); i++)
631                                 if (i >= T6BUF_SIZE)
632                                         break;
633                         lock->l_len = atol(buf + i);
634                         if (lock->l_len <= 0)
635                                 break;
636                         lock->l_type = tags[v].mode;
637                         return 1;
638                 }
639         }
640         fprintf(stderr, "Invalid line: %s\n", buf);
641         return 0;
642 }
643
644 /*
645  *      Read command from stdin then enqueue a lock
646  *
647  *      [W|R|U]sss,lll
648  *      W: write R: read U: unlock
649  *      sss: start of range
650  *      lll: length of range
651  *
652  *      for example:
653  *              W1,100          # add a write lock from 1 to 100
654  *              R100,100        # add a read lock from 100 to 199
655  */
656 static int t6(int argc, char *argv[])
657 {
658         struct flock lock = {
659                 .l_whence = SEEK_SET,
660         };
661
662         int fd, rc = 0;
663         char buf[T6BUF_SIZE+1];
664         double stime;
665
666         if (argc < 3) {
667                 fprintf(stderr, "usage: flocks_test 6 file\n");
668                 return EXIT_FAILURE;
669         }
670
671         fd = open(argv[2], O_RDWR);
672         if (fd < 0) {
673                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
674                 return EXIT_FAILURE;
675         }
676
677         memset(buf, '\0', T6BUF_SIZE + 1);
678         stime = now();
679         while (fgets(buf, T6BUF_SIZE, stdin)) {
680                 if (set_lock(&lock, buf)) {
681                         rc = t_fcntl(fd, F_SETLKW, &lock);
682                         if (rc != 0) {
683                                 fprintf(stderr, "%d: cannot set lock: %s\n",
684                                         getpid(), strerror(errno));
685                                 rc = EXIT_FAILURE;
686                                 break;
687                         }
688                 }
689         }
690         close(fd);
691         printf("Time for processing %.03lfs\n", now() - stime);
692         return rc;
693 }
694
695 /** ==============================================================
696  * program entry
697  */
698 static void usage(void)
699 {
700         fprintf(stderr,
701                 "usage: flocks_test test# [corresponding arguments]\n");
702 }
703
704 int main(int argc, char *argv[])
705 {
706         int rc = EXIT_SUCCESS;
707
708         if (argc < 2) {
709                 usage();
710                 exit(EXIT_FAILURE);
711         }
712
713         switch (atoi(argv[1])) {
714         case 1:
715                 rc = t1(argc, argv);
716                 break;
717         case 2:
718                 rc = t2(argc, argv);
719                 break;
720         case 3:
721                 rc = t3(argc, argv);
722                 break;
723         case 4:
724                 rc = t4(argc, argv);
725                 break;
726         case 5:
727                 rc = t5(argc, argv);
728                 break;
729         case 6:
730                 rc = t6(argc, argv);
731                 break;
732         default:
733                 fprintf(stderr, "unknown test number '%s'\n", argv[1]);
734                 break;
735         }
736
737         if (rc)
738                 fprintf(stderr, "exiting with rc = %d\n", rc);
739         return rc;
740 }