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
11static 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
23static 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
35int 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("[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);
52	if (rc == 0) {
53		ksft_print_msg
54		    ("[SUCCESS] set OFD read lock on first fd\n");
55	} else {
56		ksft_print_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(fd2, &fl);
64	if (rc != 0)
65		return -1;
66	if (fl.l_type != F_UNLCK) {
67		ksft_print_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(fd2, &fl);
75	if (rc != 0)
76		return -1;
77	if (fl.l_type != F_UNLCK) {
78		ksft_print_msg
79		    ("[SUCCESS] read and write locks conflicted\n");
80	} else {
81		ksft_print_msg
82		    ("[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);
90	if (rc != 0) {
91		ksft_print_msg
92		    ("[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		    ("[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		    ("[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, &fl2);
109	if (rc != 0) {
110		ksft_print_msg
111		    ("[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		    ("[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("[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(fd2, &fl);
126	if (fl.l_type != F_UNLCK) {
127		ksft_print_msg
128		    ("[FAIL] F_OFD_GETLK with F_UNLCK return lock info from another fd\n");
129		return -1;
130	}
131	return 0;
132}
133