1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #define _GNU_SOURCE |
4 | #include <fcntl.h> |
5 | #include <assert.h> |
6 | #include <stdio.h> |
7 | #include <unistd.h> |
8 | #include <string.h> |
9 | #include "../kselftest.h" |
10 | |
11 | static int lock_set(int fd, struct flock *fl) |
12 | { |
13 | int ret; |
14 | |
15 | fl->l_pid = 0; // needed for OFD locks |
16 | fl->l_whence = SEEK_SET; |
17 | ret = fcntl(fd, F_OFD_SETLK, fl); |
18 | if (ret) |
19 | perror("fcntl()" ); |
20 | return ret; |
21 | } |
22 | |
23 | static int lock_get(int fd, struct flock *fl) |
24 | { |
25 | int ret; |
26 | |
27 | fl->l_pid = 0; // needed for OFD locks |
28 | fl->l_whence = SEEK_SET; |
29 | ret = fcntl(fd, F_OFD_GETLK, fl); |
30 | if (ret) |
31 | perror("fcntl()" ); |
32 | return ret; |
33 | } |
34 | |
35 | int main(void) |
36 | { |
37 | int rc; |
38 | struct flock fl, fl2; |
39 | int fd = open("/tmp/aa" , O_RDWR | O_CREAT | O_EXCL, 0600); |
40 | int fd2 = open("/tmp/aa" , O_RDONLY); |
41 | |
42 | unlink("/tmp/aa" ); |
43 | assert(fd != -1); |
44 | assert(fd2 != -1); |
45 | ksft_print_msg(msg: "[INFO] opened fds %i %i\n" , fd, fd2); |
46 | |
47 | /* Set some read lock */ |
48 | fl.l_type = F_RDLCK; |
49 | fl.l_start = 5; |
50 | fl.l_len = 3; |
51 | rc = lock_set(fd, fl: &fl); |
52 | if (rc == 0) { |
53 | ksft_print_msg |
54 | (msg: "[SUCCESS] set OFD read lock on first fd\n" ); |
55 | } else { |
56 | ksft_print_msg(msg: "[FAIL] to set OFD read lock on first fd\n" ); |
57 | return -1; |
58 | } |
59 | /* Make sure read locks do not conflict on different fds. */ |
60 | fl.l_type = F_RDLCK; |
61 | fl.l_start = 5; |
62 | fl.l_len = 1; |
63 | rc = lock_get(fd: fd2, fl: &fl); |
64 | if (rc != 0) |
65 | return -1; |
66 | if (fl.l_type != F_UNLCK) { |
67 | ksft_print_msg(msg: "[FAIL] read locks conflicted\n" ); |
68 | return -1; |
69 | } |
70 | /* Make sure read/write locks do conflict on different fds. */ |
71 | fl.l_type = F_WRLCK; |
72 | fl.l_start = 5; |
73 | fl.l_len = 1; |
74 | rc = lock_get(fd: fd2, fl: &fl); |
75 | if (rc != 0) |
76 | return -1; |
77 | if (fl.l_type != F_UNLCK) { |
78 | ksft_print_msg |
79 | (msg: "[SUCCESS] read and write locks conflicted\n" ); |
80 | } else { |
81 | ksft_print_msg |
82 | (msg: "[SUCCESS] read and write locks not conflicted\n" ); |
83 | return -1; |
84 | } |
85 | /* Get info about the lock on first fd. */ |
86 | fl.l_type = F_UNLCK; |
87 | fl.l_start = 5; |
88 | fl.l_len = 1; |
89 | rc = lock_get(fd, fl: &fl); |
90 | if (rc != 0) { |
91 | ksft_print_msg |
92 | (msg: "[FAIL] F_OFD_GETLK with F_UNLCK not supported\n" ); |
93 | return -1; |
94 | } |
95 | if (fl.l_type != F_UNLCK) { |
96 | ksft_print_msg |
97 | (msg: "[SUCCESS] F_UNLCK test returns: locked, type %i pid %i len %zi\n" , |
98 | fl.l_type, fl.l_pid, fl.l_len); |
99 | } else { |
100 | ksft_print_msg |
101 | (msg: "[FAIL] F_OFD_GETLK with F_UNLCK did not return lock info\n" ); |
102 | return -1; |
103 | } |
104 | /* Try the same but by locking everything by len==0. */ |
105 | fl2.l_type = F_UNLCK; |
106 | fl2.l_start = 0; |
107 | fl2.l_len = 0; |
108 | rc = lock_get(fd, fl: &fl2); |
109 | if (rc != 0) { |
110 | ksft_print_msg |
111 | (msg: "[FAIL] F_OFD_GETLK with F_UNLCK not supported\n" ); |
112 | return -1; |
113 | } |
114 | if (memcmp(&fl, &fl2, sizeof(fl))) { |
115 | ksft_print_msg |
116 | (msg: "[FAIL] F_UNLCK test returns: locked, type %i pid %i len %zi\n" , |
117 | fl.l_type, fl.l_pid, fl.l_len); |
118 | return -1; |
119 | } |
120 | ksft_print_msg(msg: "[SUCCESS] F_UNLCK with len==0 returned the same\n" ); |
121 | /* Get info about the lock on second fd - no locks on it. */ |
122 | fl.l_type = F_UNLCK; |
123 | fl.l_start = 0; |
124 | fl.l_len = 0; |
125 | lock_get(fd: fd2, fl: &fl); |
126 | if (fl.l_type != F_UNLCK) { |
127 | ksft_print_msg |
128 | (msg: "[FAIL] F_OFD_GETLK with F_UNLCK return lock info from another fd\n" ); |
129 | return -1; |
130 | } |
131 | return 0; |
132 | } |
133 | |