Skip to content

Commit 875d303

Browse files
committed
[dfs][utest] Add POSIX record-lock coverage
Cover positive/negative l_len, overlapping GETLK conflicts, interrupted F_SETLKW, split/merge, and close/dup2 lock release on tmpfs.
1 parent 3780805 commit 875d303

3 files changed

Lines changed: 353 additions & 0 deletions

File tree

components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@ menu "Tmpfs Testcase"
33
config RT_UTEST_TMPFS_CP
44
bool "tmpfs cp test"
55
default n
6+
7+
config RT_UTEST_DFS_RECORD_LOCK
8+
bool "DFS POSIX record lock test"
9+
default n
10+
depends on RT_USING_POSIX_FS && RT_USING_DFS_TMPFS
11+
help
12+
Enable POSIX advisory record-lock tests on tmpfs.
13+
614
endmenu

components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ CPPPATH = [cwd]
88
if GetDepend(['RT_USING_SMART','RT_UTEST_TMPFS_CP']):
99
src += ['tmpfs.c']
1010

11+
if GetDepend(['RT_UTEST_DFS_RECORD_LOCK']):
12+
src += ['tc_record_lock.c']
13+
1114
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
1215

1316
Return('group')
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/*
2+
* Copyright (c) 2006-2026 RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2026-08-23 Bernard POSIX record-lock coverage on tmpfs
9+
*/
10+
11+
#include <rtthread.h>
12+
#include <errno.h>
13+
#include <fcntl.h>
14+
#include <unistd.h>
15+
#include <string.h>
16+
17+
#ifdef RT_USING_SIGNALS
18+
#include <signal.h>
19+
#endif
20+
21+
#ifdef RT_USING_SMART
22+
#include <lwp.h>
23+
rt_err_t sys_dup2(int oldfd, int newfd);
24+
#endif
25+
26+
#include "utest.h"
27+
#include "utest_assert.h"
28+
29+
#define LOCK_PATH "/tmp/record-lock-utest"
30+
#define LOCK_PAYLOAD "0123456789abcdef"
31+
32+
struct lock_waiter_arg
33+
{
34+
struct flock fl;
35+
int result;
36+
int err;
37+
rt_sem_t started;
38+
rt_sem_t done;
39+
};
40+
41+
static void fill_lock(struct flock *fl, short type, short whence, off_t start, off_t len)
42+
{
43+
rt_memset(fl, 0, sizeof(*fl));
44+
fl->l_type = type;
45+
fl->l_whence = whence;
46+
fl->l_start = start;
47+
fl->l_len = len;
48+
}
49+
50+
static int set_lock(int fd, int cmd, short type, short whence, off_t start, off_t len)
51+
{
52+
struct flock fl;
53+
54+
fill_lock(&fl, type, whence, start, len);
55+
return fcntl(fd, cmd, &fl);
56+
}
57+
58+
static int get_lock(int fd, struct flock *fl, short type, short whence, off_t start, off_t len)
59+
{
60+
fill_lock(fl, type, whence, start, len);
61+
return fcntl(fd, F_GETLK, fl);
62+
}
63+
64+
static int create_lock_file(void)
65+
{
66+
int fd;
67+
68+
unlink(LOCK_PATH);
69+
fd = open(LOCK_PATH, O_CREAT | O_RDWR | O_TRUNC, 0666);
70+
uassert_true(fd >= 0);
71+
uassert_int_equal(write(fd, LOCK_PAYLOAD, sizeof(LOCK_PAYLOAD) - 1),
72+
sizeof(LOCK_PAYLOAD) - 1);
73+
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
74+
return fd;
75+
}
76+
77+
#ifdef RT_USING_SMART
78+
static struct rt_lwp *bind_owner(void)
79+
{
80+
struct rt_lwp *lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
81+
82+
uassert_not_null(lwp);
83+
rt_thread_self()->lwp = lwp;
84+
return lwp;
85+
}
86+
87+
static void unbind_owner(struct rt_lwp *lwp)
88+
{
89+
rt_thread_self()->lwp = RT_NULL;
90+
if (lwp)
91+
{
92+
/* Drop create() + pid-table references; leftover pid slots are test-only. */
93+
lwp_ref_dec(lwp);
94+
lwp_ref_dec(lwp);
95+
}
96+
}
97+
#endif
98+
99+
static void test_positive_negative_len(void)
100+
{
101+
int fd = create_lock_file();
102+
struct flock fl;
103+
104+
uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 4), 0);
105+
uassert_int_equal(get_lock(fd, &fl, F_WRLCK, SEEK_SET, 0, 4), 0);
106+
uassert_int_equal(fl.l_type, F_UNLCK);
107+
108+
uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 4, -4), 0);
109+
uassert_int_equal(set_lock(fd, F_SETLK, F_RDLCK, SEEK_SET, 8, -4), 0);
110+
uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0);
111+
uassert_int_equal(close(fd), 0);
112+
uassert_int_equal(unlink(LOCK_PATH), 0);
113+
}
114+
115+
static void test_conflict_and_getlk(void)
116+
{
117+
#ifdef RT_USING_SMART
118+
int fd_a;
119+
int fd_b;
120+
struct flock fl;
121+
struct rt_lwp *owner_a;
122+
struct rt_lwp *owner_b;
123+
124+
owner_a = bind_owner();
125+
fd_a = create_lock_file();
126+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
127+
128+
owner_b = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
129+
uassert_not_null(owner_b);
130+
rt_thread_self()->lwp = owner_b;
131+
fd_b = open(LOCK_PATH, O_RDWR, 0);
132+
uassert_true(fd_b >= 0);
133+
134+
errno = 0;
135+
uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 4), -1);
136+
uassert_int_equal(errno, -EAGAIN);
137+
138+
uassert_int_equal(get_lock(fd_b, &fl, F_WRLCK, SEEK_SET, 0, 4), 0);
139+
uassert_int_equal(fl.l_type, F_WRLCK);
140+
uassert_int_equal(fl.l_whence, SEEK_SET);
141+
uassert_int_equal(fl.l_start, 0);
142+
uassert_int_equal(fl.l_len, 8);
143+
uassert_int_equal(fl.l_pid, owner_a->pid);
144+
145+
rt_thread_self()->lwp = owner_a;
146+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0);
147+
148+
rt_thread_self()->lwp = owner_b;
149+
uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 4), 0);
150+
uassert_int_equal(set_lock(fd_b, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0);
151+
uassert_int_equal(close(fd_b), 0);
152+
153+
rt_thread_self()->lwp = owner_a;
154+
uassert_int_equal(close(fd_a), 0);
155+
unbind_owner(owner_a);
156+
unbind_owner(owner_b);
157+
uassert_int_equal(unlink(LOCK_PATH), 0);
158+
#else
159+
uassert_true(1);
160+
#endif
161+
}
162+
163+
static void waiter_entry(void *parameter)
164+
{
165+
struct lock_waiter_arg *arg = (struct lock_waiter_arg *)parameter;
166+
int fd;
167+
#ifdef RT_USING_SMART
168+
struct rt_lwp *owner = bind_owner();
169+
#endif
170+
171+
#ifdef RT_USING_SIGNALS
172+
rt_signal_install(SIGUSR1, RT_NULL);
173+
rt_signal_unmask(SIGUSR1);
174+
#endif
175+
fd = open(LOCK_PATH, O_RDWR, 0);
176+
if (fd < 0)
177+
{
178+
arg->result = -1;
179+
arg->err = errno;
180+
rt_sem_release(arg->started);
181+
rt_sem_release(arg->done);
182+
#ifdef RT_USING_SMART
183+
unbind_owner(owner);
184+
#endif
185+
return;
186+
}
187+
188+
rt_sem_release(arg->started);
189+
arg->result = fcntl(fd, F_SETLKW, &arg->fl);
190+
arg->err = errno;
191+
close(fd);
192+
#ifdef RT_USING_SMART
193+
unbind_owner(owner);
194+
#endif
195+
rt_sem_release(arg->done);
196+
}
197+
198+
static void test_interrupted_setlkw(void)
199+
{
200+
#if defined(RT_USING_SMART) && defined(RT_USING_SIGNALS)
201+
int fd_a;
202+
rt_thread_t waiter;
203+
struct lock_waiter_arg arg;
204+
struct rt_semaphore started;
205+
struct rt_semaphore done;
206+
struct rt_lwp *owner;
207+
208+
owner = bind_owner();
209+
fd_a = create_lock_file();
210+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 4), 0);
211+
212+
rt_sem_init(&started, "rlks", 0, RT_IPC_FLAG_FIFO);
213+
rt_sem_init(&done, "rlkw", 0, RT_IPC_FLAG_FIFO);
214+
fill_lock(&arg.fl, F_WRLCK, SEEK_SET, 0, 4);
215+
arg.result = 0;
216+
arg.err = 0;
217+
arg.started = &started;
218+
arg.done = &done;
219+
220+
waiter = rt_thread_create("rlkw", waiter_entry, &arg, 4096,
221+
RT_SCHED_PRIV(rt_thread_self()).current_priority + 1, 10);
222+
uassert_not_null(waiter);
223+
rt_thread_startup(waiter);
224+
uassert_int_equal(rt_sem_take(&started, RT_TICK_PER_SECOND), RT_EOK);
225+
rt_thread_mdelay(10);
226+
uassert_int_equal(rt_thread_kill(waiter, SIGUSR1), RT_EOK);
227+
uassert_int_equal(rt_sem_take(&done, RT_TICK_PER_SECOND), RT_EOK);
228+
uassert_int_equal(arg.result, -1);
229+
uassert_int_equal(arg.err, -EINTR);
230+
231+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0);
232+
uassert_int_equal(close(fd_a), 0);
233+
unbind_owner(owner);
234+
rt_sem_detach(&started);
235+
rt_sem_detach(&done);
236+
uassert_int_equal(unlink(LOCK_PATH), 0);
237+
#else
238+
uassert_true(1);
239+
#endif
240+
}
241+
242+
static void test_split_merge(void)
243+
{
244+
int fd = create_lock_file();
245+
struct flock fl;
246+
247+
uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 16), 0);
248+
uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 4, 4), 0);
249+
uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 4, 4), 0);
250+
251+
uassert_int_equal(get_lock(fd, &fl, F_WRLCK, SEEK_SET, 0, 16), 0);
252+
uassert_int_equal(fl.l_type, F_UNLCK);
253+
254+
uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0);
255+
uassert_int_equal(close(fd), 0);
256+
uassert_int_equal(unlink(LOCK_PATH), 0);
257+
}
258+
259+
static void test_close_and_dup_release(void)
260+
{
261+
#ifdef RT_USING_SMART
262+
int fd_a;
263+
int fd_b;
264+
int fd_replace;
265+
struct flock fl;
266+
struct rt_lwp *owner_a;
267+
struct rt_lwp *owner_b;
268+
269+
owner_a = bind_owner();
270+
fd_a = create_lock_file();
271+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
272+
273+
owner_b = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
274+
uassert_not_null(owner_b);
275+
rt_thread_self()->lwp = owner_b;
276+
fd_b = open(LOCK_PATH, O_RDWR, 0);
277+
uassert_true(fd_b >= 0);
278+
uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 8), -1);
279+
280+
rt_thread_self()->lwp = owner_a;
281+
uassert_int_equal(close(fd_a), 0);
282+
283+
rt_thread_self()->lwp = owner_b;
284+
uassert_int_equal(set_lock(fd_b, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
285+
286+
rt_thread_self()->lwp = owner_a;
287+
fd_a = open(LOCK_PATH, O_RDWR, 0);
288+
uassert_true(fd_a >= 0);
289+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), -1);
290+
291+
rt_thread_self()->lwp = owner_b;
292+
fd_replace = open(LOCK_PATH, O_RDWR, 0);
293+
uassert_true(fd_replace >= 0);
294+
uassert_int_equal(sys_dup2(fd_replace, fd_b), fd_b);
295+
296+
rt_thread_self()->lwp = owner_a;
297+
uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
298+
uassert_int_equal(get_lock(fd_a, &fl, F_WRLCK, SEEK_SET, 0, 8), 0);
299+
uassert_int_equal(fl.l_type, F_UNLCK);
300+
301+
uassert_int_equal(close(fd_a), 0);
302+
rt_thread_self()->lwp = owner_b;
303+
uassert_int_equal(close(fd_b), 0);
304+
uassert_int_equal(close(fd_replace), 0);
305+
306+
unbind_owner(owner_a);
307+
unbind_owner(owner_b);
308+
uassert_int_equal(unlink(LOCK_PATH), 0);
309+
#else
310+
int fd = create_lock_file();
311+
312+
uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
313+
uassert_int_equal(close(fd), 0);
314+
fd = open(LOCK_PATH, O_RDWR, 0);
315+
uassert_true(fd >= 0);
316+
uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0);
317+
uassert_int_equal(close(fd), 0);
318+
uassert_int_equal(unlink(LOCK_PATH), 0);
319+
#endif
320+
}
321+
322+
static rt_err_t utest_tc_init(void)
323+
{
324+
unlink(LOCK_PATH);
325+
return RT_EOK;
326+
}
327+
328+
static rt_err_t utest_tc_cleanup(void)
329+
{
330+
unlink(LOCK_PATH);
331+
return RT_EOK;
332+
}
333+
334+
static void testcase(void)
335+
{
336+
UTEST_UNIT_RUN(test_positive_negative_len);
337+
UTEST_UNIT_RUN(test_conflict_and_getlk);
338+
UTEST_UNIT_RUN(test_interrupted_setlkw);
339+
UTEST_UNIT_RUN(test_split_merge);
340+
UTEST_UNIT_RUN(test_close_and_dup_release);
341+
}
342+
UTEST_TC_EXPORT(testcase, "components.dfs.record_lock", utest_tc_init, utest_tc_cleanup, 30);

0 commit comments

Comments
 (0)