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