generic/486: Get rid of the redundant error=%d printing
[xfstests-dev.git] / src / t_ofd_locks.c
1 #ifndef _GNU_SOURCE
2 #define _GNU_SOURCE
3 #endif
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <sched.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/ipc.h>
16 #include <sys/sem.h>
17
18 /*
19  * In distributions that do not have these macros ready in glibc-headers,
20  * compilation fails. Adding them here to avoid build errors, relevant tests
21  * would fail at the helper which requires OFD locks support and notrun if the
22  * kernel does not support OFD locks. If the kernel does support OFD locks, we
23  * are good to go.
24  */
25 #ifndef F_OFD_GETLK
26 #define F_OFD_GETLK    36
27 #endif
28
29 #ifndef F_OFD_SETLK
30 #define F_OFD_SETLK    37
31 #endif
32
33 #ifndef F_OFD_SETLKW
34 #define F_OFD_SETLKW   38
35 #endif
36
37 /*
38  * Usually we run getlk routine after running setlk routine
39  * in background. However, getlk could be executed before setlk
40  * sometimes, which is invalid for our tests. So we use semaphore
41  * to synchronize between getlk and setlk.
42  *
43  * setlk routine:                                * getlk routine:
44  *                                               *
45  *   start                                       *   start
46  *     |                                         *     |
47  *  open file                                    *  open file
48  *     |                                         *     |
49  *  init sem                                     *     |
50  *     |                                         *     |
51  * wait init sem done                            * wait init sem done
52  *     |                                         *     |
53  *   setlk                                       *     |
54  *     |                                         *     |
55  *     |------------clone()--------|             *     |
56  *     |                           |             *     |
57  *     |(parent)            (child)|             *     |
58  *     |                           |             *     |
59  *     |                      close fd           *     |
60  *     |                           |             *     |
61  *     |                     set sem0=0          * wait sem0==0
62  *     |                           |             *     |
63  *     |                           |             *   getlk
64  *     |                           |             *     |
65  *  wait sem1==0                   |             *  set sem1=0
66  *     |                           |             *     |
67  *   wait child                    |             *     |
68  *     |                           |             *  check result
69  *     |                           |             *     |
70  *    exit                       exit            *    exit
71  */
72
73 static int fd;
74 static int semid;
75
76 /* This is required by semctl to set semaphore value */
77 union semun {
78        int              val;    /* Value for SETVAL */
79        struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
80        unsigned short  *array;  /* Array for GETALL, SETALL */
81        struct seminfo  *__buf;  /* Buffer for IPC_INFO
82                                    (Linux-specific) */
83 };
84
85 static void err_exit(char *op, int errn)
86 {
87         fprintf(stderr, "%s: %s\n", op, strerror(errn));
88         if (fd > 0)
89                 close(fd);
90         if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1)
91                 perror("exit rmid");
92         exit(errn);
93 }
94
95 /*
96  * Flags that used to specify operation details.
97  * They can be specified via command line options.
98  *
99  * option: -P
100  * posix : 1 <--> test posix lock
101  *         0 <--> test OFD lock (default)
102  *
103  * option: -s/-g
104  * lock_cmd : 1 <--> setlk (default)
105  *            0 <--> getlk
106  *
107  * option: -r/-w
108  * lock_rw : 1 <--> set/get wrlck (default)
109  *           0 <--> set/get rdlck
110  *
111  * option: -o num
112  * lock_start : l_start to getlk
113  *
114  * option: -F
115  * clone_fs : clone with CLONE_FILES
116  *
117  * option: -d
118  * use_dup : dup and close to setup condition in setlk
119  *
120  * option: -R/-W
121  * open_rw : 1 <--> open file RDWR (default)
122  *           0 <--> open file RDONLY
123  *
124  * This option is for _require_ofd_locks helper, just do
125  * fcntl setlk then return errno.
126  * option: -t
127  * testrun : 1 <--> this is a testrun, return after setlk
128  *           0 <--> this is not a testrun, run as usual
129  */
130
131 static void usage(char *arg0)
132 {
133         printf("Usage: %s [-sgrwo:l:RWPtFd] filename\n", arg0);
134         printf("\t-s/-g : to setlk or to getlk\n");
135         printf("\t-P : POSIX locks\n");
136         printf("\t-F : clone with CLONE_FILES in setlk to setup test condition\n");
137         printf("\t-d : dup and close in setlk\n");
138         printf("\twithout both -F/d, use clone without CLONE_FILES\n");
139         printf("\t-r/-w : set/get rdlck/wrlck\n");
140         printf("\t-o num : offset start to lock, default 0\n");
141         printf("\t-l num : lock length, default 10\n");
142         printf("\t-R/-W : open file RDONLY/RDWR\n\n");
143         printf("\tUsually we run a setlk routine in background and then\n");
144         printf("\trun a getlk routine to check. They must be paired, or\n");
145         printf("\ttest will hang.\n\n");
146         exit(0);
147 }
148
149 #define STACK_SIZE (1024 * 1024)
150 static char child_stack[STACK_SIZE] __attribute__((aligned));
151
152 static int child_fn(void* p)
153 {
154         union semun semu;
155         int cfd = *(int *)p;
156
157         /* close relative fd */
158         if (cfd > 0 && close(cfd) == -1)
159                 perror("close in child");
160
161         /* set sem0 = 0 (setlk and close fd done) */
162         semu.val = 0;
163         if (semctl(semid, 0, SETVAL, semu) == -1)
164                 err_exit("set sem0 0", errno);
165
166         return 0;
167 }
168
169 int main(int argc, char **argv)
170 {
171         int posix = 0;
172         int lock_cmd = 1;
173         int lock_rw = 1;
174         int lock_start = 0;
175         int lock_l = 10;
176         int open_rw = 1;
177         int clone_fs = 0;
178         int use_dup = 0;
179         int testrun = 0;
180         int setlk_macro = F_OFD_SETLKW;
181         int getlk_macro = F_OFD_GETLK;
182         struct timespec ts;
183         key_t semkey;
184         unsigned short vals[2];
185         union semun semu;
186         struct semid_ds sem_ds;
187         struct sembuf sop;
188         int opt, ret, retry;
189
190         while((opt = getopt(argc, argv, "sgrwo:l:PRWtFd")) != -1) {
191                 switch(opt) {
192                 case 's':
193                         lock_cmd = 1;
194                         break;
195                 case 'g':
196                         lock_cmd = 0;
197                         break;
198                 case 'r':
199                         lock_rw = 0;
200                         break;
201                 case 'w':
202                         lock_rw = 1;
203                         break;
204                 case 'o':
205                         lock_start = atoi(optarg);
206                         break;
207                 case 'l':
208                         lock_l = atoi(optarg);
209                         break;
210                 case 'P':
211                         posix = 1;
212                         break;
213                 case 'R':
214                         open_rw = 0;
215                         break;
216                 case 'W':
217                         open_rw = 1;
218                         break;
219                 case 't':
220                         testrun = 1;
221                         break;
222                 case 'F':
223                         clone_fs = 1;
224                         break;
225                 case 'd':
226                         use_dup = 1;
227                         break;
228                 default:
229                         usage(argv[0]);
230                         return -1;
231                 }
232         }
233
234         if (optind >= argc) {
235                 usage(argv[0]);
236                 return -1;
237         }
238
239         struct flock flk = {
240                 .l_whence = SEEK_SET,
241                 .l_start = lock_start,
242                 .l_len = lock_l,
243                 .l_type = F_RDLCK,
244         };
245
246         if (posix == 0) {
247                 /* OFD lock requires l_pid to be zero */
248                 flk.l_pid = 0;
249                 setlk_macro = F_OFD_SETLKW;
250                 getlk_macro = F_OFD_GETLK;
251         } else {
252                 setlk_macro = F_SETLKW;
253                 getlk_macro = F_GETLK;
254         }
255
256         if (lock_rw == 1)
257                 flk.l_type = F_WRLCK;
258         else
259                 flk.l_type = F_RDLCK;
260
261         if (open_rw == 0)
262                 fd = open(argv[optind], O_RDONLY);
263         else
264                 fd = open(argv[optind], O_RDWR);
265         if (fd == -1)
266                 err_exit("open", errno);
267
268         /*
269          * In a testun, we do a fcntl getlk call and exit
270          * immediately no matter it succeeds or not.
271          */
272         if (testrun == 1) {
273                 fcntl(fd, F_OFD_GETLK, &flk);
274                 err_exit("test_ofd_getlk", errno);
275         }
276
277         if((semkey = ftok(argv[optind], 255)) == -1)
278                 err_exit("ftok", errno);
279
280         /* setlk, and always init the semaphore at setlk time */
281         if (lock_cmd == 1) {
282                 /*
283                  * Init the semaphore, with a key related to the testfile.
284                  * getlk routine will wait untill this sem has been created and
285                  * iniialized.
286                  *
287                  * We must make sure the semaphore set is newly created, rather
288                  * then the one left from last run. In which case getlk will
289                  * exit immediately and left setlk routine waiting forever.
290                  * Also because newly created semaphore has zero sem_otime,
291                  * which is used here to sync with getlk routine.
292                  */
293                 retry = 0;
294                 do {
295                         semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL);
296                         if (semid < 0 && errno == EEXIST) {
297                                 /* remove sem set after one round of test */
298                                 if (semctl(semid, 2, IPC_RMID, semu) == -1)
299                                         err_exit("rmid 0", errno);
300                                 retry++;
301                         } else if (semid < 0)
302                                 err_exit("semget", errno);
303                         else
304                                 retry = 10;
305                 } while (retry < 5);
306                 /* We can't create a new semaphore set in 5 tries */
307                 if (retry == 5)
308                         err_exit("semget", errno);
309
310                 /* Init both new sem to 1 */
311                 vals[0] = 1;
312                 vals[1] = 1;
313                 semu.array = vals;
314                 if (semctl(semid, 2, SETALL, semu) == -1)
315                         err_exit("init sem", errno);
316                 /* Inc both new sem to 2 */
317                 sop.sem_num = 0;
318                 sop.sem_op = 1;
319                 sop.sem_flg = 0;
320                 ts.tv_sec = 5;
321                 ts.tv_nsec = 0;
322                 if (semtimedop(semid, &sop, 1, &ts) == -1)
323                         err_exit("inc sem0 2", errno);
324                 sop.sem_num = 1;
325                 sop.sem_op = 1;
326                 sop.sem_flg = 0;
327                 ts.tv_sec = 5;
328                 ts.tv_nsec = 0;
329                 if (semtimedop(semid, &sop, 1, &ts) == -1)
330                         err_exit("inc sem1 2", errno);
331
332                 /*
333                  * Wait initialization complete. semctl(2) only update
334                  * sem_ctime, semop(2) will update sem_otime.
335                  */
336                 ret = -1;
337                 do {
338                         memset(&sem_ds, 0, sizeof(sem_ds));
339                         semu.buf = &sem_ds;
340                         ret = semctl(semid, 0, IPC_STAT, semu);
341                 } while (!(ret == 0 && sem_ds.sem_otime != 0));
342
343                 /* place the lock */
344                 if (fcntl(fd, setlk_macro, &flk) < 0)
345                         err_exit("setlkw", errno);
346
347                 if (use_dup == 1) {
348                         /* dup fd and close the newfd */
349                         int dfd = dup(fd);
350                         if (dfd == -1)
351                                 err_exit("dup", errno);
352                         close(dfd);
353                         /* set sem0 = 0 (setlk and close fd done) */
354                         semu.val = 0;
355                         if (semctl(semid, 0, SETVAL, semu) == -1)
356                                 err_exit("set sem0 0", errno);
357                 } else {
358                         /*
359                          * clone a child to close the fd then tell getlk to go;
360                          * in parent we keep holding the lock till getlk done.
361                          */
362                         pid_t child_pid = 0;
363                         if (clone_fs)
364                                 child_pid = clone(child_fn, child_stack+STACK_SIZE,
365                                         CLONE_FILES|CLONE_SYSVSEM|SIGCHLD, &fd);
366                         else
367                                 child_pid = clone(child_fn, child_stack+STACK_SIZE,
368                                         CLONE_SYSVSEM|SIGCHLD, &fd);
369                         if (child_pid == -1)
370                                 err_exit("clone", errno);
371                         /* wait child done */
372                         waitpid(child_pid, NULL, 0);
373                 }
374
375                 /* "hold" lock and wait sem1 == 0 (getlk done) */
376                 sop.sem_num = 1;
377                 sop.sem_op = 0;
378                 sop.sem_flg = 0;
379                 ts.tv_sec = 5;
380                 ts.tv_nsec = 0;
381                 if (semtimedop(semid, &sop, 1, &ts) == -1)
382                         err_exit("wait sem1 0", errno);
383
384                 /* remove sem set after one round of test */
385                 if (semctl(semid, 2, IPC_RMID, semu) == -1)
386                         err_exit("rmid", errno);
387                 close(fd);
388                 exit(0);
389         }
390
391         /* getlck */
392         if (lock_cmd == 0) {
393                 /* wait sem created and initialized */
394                 retry = 5;
395                 do {
396                         semid = semget(semkey, 2, 0);
397                         if (semid != -1)
398                                 break;
399                         if (errno == ENOENT && retry) {
400                                 sleep(1);
401                                 retry--;
402                                 continue;
403                         } else {
404                                 err_exit("getlk_semget", errno);
405                         }
406                 } while (1);
407                 do {
408                         memset(&sem_ds, 0, sizeof(sem_ds));
409                         semu.buf = &sem_ds;
410                         ret = semctl(semid, 0, IPC_STAT, semu);
411                 } while (!(ret == 0 && sem_ds.sem_otime != 0));
412
413                 /* wait sem0 == 0 (setlk and close fd done) */
414                 sop.sem_num = 0;
415                 sop.sem_op = 0;
416                 sop.sem_flg = 0;
417                 ts.tv_sec = 5;
418                 ts.tv_nsec = 0;
419                 if (semtimedop(semid, &sop, 1, &ts) == -1)
420                         err_exit("wait sem0 0", errno);
421
422                 if (fcntl(fd, getlk_macro, &flk) < 0)
423                         err_exit("getlk", errno);
424
425                 /* set sem1 = 0 (getlk done) */
426                 semu.val = 0;
427                 if (semctl(semid, 1, SETVAL, semu) == -1)
428                         err_exit("set sem1 0", errno);
429
430                 /* check result */
431                 switch (flk.l_type) {
432                 case F_UNLCK:
433                         printf("lock could be placed\n");
434                         break;
435                 case F_RDLCK:
436                         printf("get rdlck\n");
437                         break;
438                 case F_WRLCK:
439                         printf("get wrlck\n");
440                         break;
441                 default:
442                         printf("unknown lock type\n");
443                         break;
444                 }
445                 close(fd);
446         }
447         return 0;
448 }