Whamcloud - gitweb
LU-10885 tests: fix up flocks_test bugs and code style
[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,
117                         "unlink(%s) error: %s\n", path, strerror(errno));
118         return rc;
119 }
120
121 /** =================================================================
122  * test number 1
123  *
124  * normal flock test
125  */
126 void t1_usage(void)
127 {
128         fprintf(stderr,
129                 "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         int rc = 0;
138
139         if (argc != 5) {
140                 t1_usage();
141                 return EXIT_FAILURE;
142         }
143
144         if (!strncmp(argv[2], "on", 3)) {
145                 mount_with_flock = 1;
146         } else if (!strncmp(argv[2], "off", 4)) {
147                 mount_with_flock = 0;
148         } else {
149                 t1_usage();
150                 return EXIT_FAILURE;
151         }
152
153         fd = open(argv[4], O_RDWR);
154         if (fd < 0) {
155                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[4],
156                         strerror(errno));
157                 return EXIT_FAILURE;
158         }
159
160         if (!strncmp(argv[3], "-c", 3)) {
161                 struct flock fl;
162
163                 fl.l_type = F_RDLCK;
164                 fl.l_whence = SEEK_SET;
165                 fl.l_start = 0;
166                 fl.l_len = 1;
167
168                 error = fcntl(fd, F_SETLK, &fl);
169         } else if (!strncmp(argv[3], "-l", 3)) {
170                 error = lockf(fd, F_LOCK, 1);
171         } else if (!strncmp(argv[3], "-f", 3)) {
172                 error = flock(fd, LOCK_EX);
173         } else {
174                 t1_usage();
175                 rc = EXIT_FAILURE;
176                 goto out;
177         }
178
179         if (mount_with_flock)
180                 rc = ((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
181         else
182                 rc = ((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
183
184 out:
185         if (fd >= 0)
186                 close(fd);
187         return rc;
188 }
189
190 /** ===============================================================
191  * test number 2
192  *
193  * 2 threads flock ops interweave
194  */
195 struct thread_info {
196         struct flock *lock;
197         int fd;
198         int rc;
199 } th_data;
200
201 void *t2_thread1(void *arg)
202 {
203         struct thread_info *ti = arg;
204         struct flock *lock = ti->lock;
205         int fd = ti->fd;
206
207         printf("thread 1: set write lock (blocking): rc = %d\n", ti->rc);
208         lock->l_type = F_WRLCK;
209         t_fcntl(fd, F_SETLKW, lock);
210         printf("thread 1: set write lock done: rc = %d\n", ti->rc);
211         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
212         printf("thread 1: unlock: rc = %d\n", ti->rc);
213         lock->l_type = F_UNLCK;
214         ti->rc += t_fcntl(fd, F_SETLK, lock);
215         printf("thread 1: unlock done: rc = %d\n", ti->rc);
216
217         if (ti->rc)
218                 fprintf(stdout, "thread1 exiting with rc = %d\n", ti->rc);
219         return &ti->rc;
220 }
221
222 void *t2_thread2(void *arg)
223 {
224         struct thread_info *ti = arg;
225         struct flock *lock = ti->lock;
226         int fd = ti->fd;
227
228         sleep(2);
229         printf("thread 2: unlock: rc = %d\n", ti->rc);
230         lock->l_type = F_UNLCK;
231         ti->rc += t_fcntl(fd, F_SETLK, lock);
232         printf("thread 2: unlock done: rc = %d\n", ti->rc);
233         printf("thread 2: set write lock (non-blocking): rc = %d\n", ti->rc);
234         lock->l_type = F_WRLCK;
235         ti->rc += t_fcntl(fd, F_SETLK, lock);
236         printf("thread 2: set write lock done: rc = %d\n", ti->rc);
237         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
238
239         if (ti->rc)
240                 fprintf(stdout, "thread2 exiting with rc = %d\n", ti->rc);
241         return &ti->rc;
242 }
243
244 int t2(int argc, char *argv[])
245 {
246         struct flock lock = {
247                 .l_type = F_RDLCK,
248                 .l_whence = SEEK_SET,
249         };
250         char file[MAX_PATH_LENGTH] = "";
251         int  fd, rc;
252         pthread_t th1, th2;
253         struct thread_info ti;
254
255         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
256
257         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
258         if (fd < 0) {
259                 fprintf(stderr, "error open file '%s': %s\n", file,
260                         strerror(errno));
261                 return EXIT_FAILURE;
262         }
263
264         t_fcntl(fd, F_SETFL, O_APPEND);
265         rc = t_fcntl(fd, F_GETFL);
266         if ((rc < 0) || (rc & O_APPEND) == 0) {
267                 fprintf(stderr, "error get flag: ret %x\n", rc);
268                 rc = EXIT_FAILURE;
269                 goto out;
270         }
271
272         ti.lock = &lock;
273         ti.fd   = fd;
274         ti.rc   = 0;
275         rc = pthread_create(&th1, NULL, t2_thread1, &ti);
276         if (rc) {
277                 fprintf(stderr, "error create thread 1\n");
278                 rc = EXIT_FAILURE;
279                 goto out;
280         }
281         rc = pthread_create(&th2, NULL, t2_thread2, &ti);
282         if (rc) {
283                 fprintf(stderr, "error create thread 2\n");
284                 rc = EXIT_FAILURE;
285                 goto out;
286         }
287         pthread_join(th1, NULL);
288         pthread_join(th2, NULL);
289         if (ti.rc)
290                 rc = EXIT_FAILURE;
291 out:
292         t_unlink(file);
293         close(fd);
294
295         return rc;
296 }
297
298 /** =================================================================
299  * test number 3
300  *
301  * Bug 24040: Two conflicting flocks from same process different fds should fail
302  *            two conflicting flocks from different processes but same fs
303  *            should succeed.
304  */
305 int t3(int argc, char *argv[])
306 {
307         int fd, fd2;
308         int pid;
309         int rc = EXIT_SUCCESS;
310
311         if (argc != 3) {
312                 fprintf(stderr, "usage: flocks_test 3 filename\n");
313                 return EXIT_FAILURE;
314         }
315
316         fd = open(argv[2], O_RDWR);
317         if (fd < 0) {
318                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
319                         strerror(errno));
320                 return EXIT_FAILURE;
321         }
322         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
323                 perror("first flock failed");
324                 rc = EXIT_FAILURE;
325                 goto out;
326         }
327         fd2 = open(argv[2], O_RDWR);
328         if (fd2 < 0) {
329                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
330                         strerror(errno));
331                 rc = EXIT_FAILURE;
332                 goto out;
333         }
334         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
335                 fprintf(stderr, "Second flock succeeded - FAIL\n");
336                 rc = EXIT_FAILURE;
337                 close(fd2);
338                 goto out;
339         }
340
341         close(fd2);
342
343         pid = fork();
344         if (pid == -1) {
345                 perror("fork");
346                 rc = EXIT_FAILURE;
347                 goto out;
348         }
349
350         if (pid == 0) {
351                 fd2 = open(argv[2], O_RDWR);
352                 if (fd2 < 0) {
353                         fprintf(stderr, "Couldn't open file '%s': %s\n",
354                                 argv[1], strerror(errno));
355                         rc = EXIT_FAILURE;
356                         goto out;
357                 }
358                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
359                         fprintf(stderr, "Second flock succeeded - FAIL\n");
360                         rc = EXIT_FAILURE;
361                         goto out_child;
362                 }
363                 if (flock(fd, LOCK_UN) == -1) {
364                         fprintf(stderr, "Child unlock on parent fd failed\n");
365                         rc = EXIT_FAILURE;
366                         goto out_child;
367                 }
368                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
369                         fprintf(stderr, "Relock after parent unlock failed!\n");
370                         rc = EXIT_FAILURE;
371                         goto out_child;
372                 }
373         out_child:
374                 close(fd2);
375                 exit(rc);
376         }
377
378         waitpid(pid, &rc, 0);
379 out:
380         close(fd);
381         return rc;
382 }
383
384 int t4(int argc, char *argv[])
385 {
386         struct flock lock = {
387                 .l_type = F_WRLCK,
388                 .l_whence = SEEK_SET,
389                 .l_start = 0,
390                 .l_len = 10,
391         };
392
393         int fd, fd2;
394         pid_t child_pid;
395         int child_status;
396         int rc = EXIT_SUCCESS;
397
398         if (argc != 4) {
399                 fprintf(stderr, "usage: flocks_test 4 file1 file2\n");
400                 return EXIT_FAILURE;
401         }
402
403         fd = open(argv[2], O_RDWR);
404         if (fd < 0) {
405                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
406                 return EXIT_FAILURE;
407         }
408         fd2 = open(argv[3], O_RDWR);
409         if (fd2 < 0) {
410                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
411                 rc = EXIT_FAILURE;
412                 goto out;
413         }
414
415         child_pid = fork();
416         if (child_pid < 0) {
417                 perror("fork");
418                 rc = EXIT_FAILURE;
419                 goto out;
420         }
421
422         if (child_pid == 0) {
423                 printf("%d: get lock1\n", getpid());
424                 fflush(stdout);
425                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
426                         fprintf(stderr, "%d: cannot get lock1: %s\n",
427                                 getpid(), strerror(errno));
428                         rc = EXIT_FAILURE;
429                         goto out_child;
430                 }
431                 printf("%d: done\n", getpid());
432                 sleep(3);
433                 printf("%d: get lock2\n", getpid());
434                 fflush(stdout);
435                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
436                         fprintf(stderr, "%d: cannot get lock2: %s\n",
437                                 getpid(), strerror(errno));
438
439                         if (errno == EDEADLK)
440                                 rc = EXIT_SUCCESS;
441                         else
442                                 rc = EXIT_FAILURE;
443
444                         goto out_child;
445                 }
446                 printf("%d: done\n", getpid());
447 out_child:
448                 printf("%d: exit rc=%d\n", getpid(), rc);
449                 exit(rc);
450         } else {
451                 printf("%d: get lock2\n", getpid());
452                 fflush(stdout);
453                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
454                         fprintf(stderr, "%d: cannot get lock2: %s\n",
455                                 getpid(), strerror(errno));
456                         rc = EXIT_FAILURE;
457                         goto out;
458                 }
459                 printf("%d: done\n", getpid());
460                 sleep(3);
461                 printf("%d: get lock1\n", getpid());
462                 fflush(stdout);
463                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
464                         fprintf(stderr, "%d: cannot get lock1: %s\n",
465                                 getpid(), strerror(errno));
466
467                         if (errno != EDEADLK) {
468                                 rc = EXIT_FAILURE;
469                                 goto out;
470                         }
471                 }
472                 printf("%d: done\n", getpid());
473         }
474
475         sleep(1);
476
477         if (close(fd) < 0) {
478                 fprintf(stderr, "%d: error closing file1: %s\n",
479                         getpid(), strerror(errno));
480                 rc = EXIT_FAILURE;
481         }
482
483         if (close(fd2) < 0) {
484                 fprintf(stderr, "%d: error closing file2: %s\n",
485                         getpid(), strerror(errno));
486                 rc = EXIT_FAILURE;
487         }
488
489         if (waitpid(child_pid, &child_status, 0) < 0) {
490                 fprintf(stderr, "%d: cannot get termination status of %d: %s\n",
491                         getpid(), child_pid, strerror(errno));
492                 rc = EXIT_FAILURE;
493         } else if (!WIFEXITED(child_status)) {
494                 fprintf(stderr, "%d: child %d terminated with status %d\n",
495                         getpid(), child_pid, child_status);
496                 rc = EXIT_FAILURE;
497         } else {
498                 rc = WEXITSTATUS(child_status);
499         }
500
501 out:
502         printf("%d: exit rc=%d\n", getpid(), rc);
503         return rc;
504 }
505
506 #define T5_USAGE                                                              \
507 "usage: flocks_test 5 {set|get|unlock} [read|write] [sleep N] file1\n"        \
508 "       set: F_SETLKW F_WRLCK\n"                                              \
509 "       get: F_GETLK F_WRLCK  (conflict)\n"                                   \
510 "       unlock: F_SETLKW F_UNLCK\n"                                           \
511 "       read|write: lock mode, write by default\n"                            \
512 "       sleep N: sleep for N secs after fcntl\n"                              \
513 "       file1: fcntl is called for this file\n"
514
515 int t5(int argc, char *argv[])
516 {
517         struct flock lock = {
518                 .l_type = F_WRLCK,
519                 .l_whence = SEEK_SET,
520         };
521
522         int setlk = 0, getlk = 0, unlk = 0, secs = 0;
523         int pos;
524         int fd;
525         int rc = 0;
526
527         if (argc < 4 || argc > 7) {
528                 fprintf(stderr, T5_USAGE);
529                 return EXIT_FAILURE;
530         }
531
532         if (!strncmp(argv[2], "set", 4))
533                 setlk = 1;
534         else if (!strncmp(argv[2], "get", 4))
535                 getlk = 1;
536         else if (!strncmp(argv[2], "unlock", 7))
537                 unlk = 1;
538         else {
539                 fprintf(stderr, "Wrong 2nd argument: %s\n", argv[2]);
540                 return EXIT_FAILURE;
541         }
542
543         pos = 3;
544
545         if (!strncmp(argv[pos], "read", 5)) {
546                 lock.l_type = F_RDLCK;
547                 pos++;
548         } else if (!strncmp(argv[pos], "write", 6)) {
549                 lock.l_type = F_WRLCK;
550                 pos++;
551         }
552
553         if (!strncmp(argv[pos], "sleep", 6)) {
554                 secs = atoi(argv[pos + 1]);
555                 if (secs < 0 || secs > 10) {
556                         fprintf(stderr, "Sleep argument is wrong: %s\n",
557                                 argv[pos + 1]);
558                         return EXIT_FAILURE;
559                 }
560                 pos += 2;
561         }
562
563         fd = open(argv[pos], O_RDWR);
564         if (fd < 0) {
565                 fprintf(stderr, "Couldn't open file: %s\n", argv[pos]);
566                 return EXIT_FAILURE;
567         }
568
569         fprintf(stderr, "\nFLOCKS_TEST 5: %s %s flock\n",
570                 setlk ? "SET" : getlk ? "GET" : "UNLOCK",
571                 lock.l_type == F_WRLCK ? "write" : "read");
572
573         if (setlk) {
574                 rc = t_fcntl(fd, F_SETLKW, &lock);
575         } else if (getlk) {
576                 rc = t_fcntl(fd, F_GETLK, &lock);
577         } else if (unlk) {
578                 lock.l_type = F_UNLCK;
579                 rc = t_fcntl(fd, F_SETLKW, &lock);
580         }
581
582         if (secs)
583                 sleep(secs);
584
585         close(fd);
586         return rc < 0 ? -rc : 0;
587
588 }
589
590 /** ==============================================================
591  * program entry
592  */
593 void usage(void)
594 {
595         fprintf(stderr,
596                 "usage: flocks_test test# [corresponding arguments]\n");
597 }
598
599 int main(int argc, char *argv[])
600 {
601         int rc = EXIT_SUCCESS;
602
603         if (argc < 2) {
604                 usage();
605                 exit(EXIT_FAILURE);
606         }
607
608         switch (atoi(argv[1])) {
609         case 1:
610                 rc = t1(argc, argv);
611                 break;
612         case 2:
613                 rc = t2(argc, argv);
614                 break;
615         case 3:
616                 rc = t3(argc, argv);
617                 break;
618         case 4:
619                 rc = t4(argc, argv);
620                 break;
621         case 5:
622                 rc = t5(argc, argv);
623                 break;
624         default:
625                 fprintf(stderr, "unknown test number '%s'\n", argv[1]);
626                 break;
627         }
628
629         if (rc)
630                 fprintf(stderr, "exiting with rc = %d\n", rc);
631         return rc;
632 }