@@ -1659,15 +1659,21 @@ TEST_F_FORK(layout1, execute)
16591659
16601660TEST_F_FORK (layout1 , link )
16611661{
1662- const struct rule rules [] = {
1662+ const struct rule layer1 [] = {
16631663 {
16641664 .path = dir_s1d2 ,
16651665 .access = LANDLOCK_ACCESS_FS_MAKE_REG ,
16661666 },
16671667 {},
16681668 };
1669- const int ruleset_fd =
1670- create_ruleset (_metadata , rules [0 ].access , rules );
1669+ const struct rule layer2 [] = {
1670+ {
1671+ .path = dir_s1d3 ,
1672+ .access = LANDLOCK_ACCESS_FS_REMOVE_FILE ,
1673+ },
1674+ {},
1675+ };
1676+ int ruleset_fd = create_ruleset (_metadata , layer1 [0 ].access , layer1 );
16711677
16721678 ASSERT_LE (0 , ruleset_fd );
16731679
@@ -1680,14 +1686,30 @@ TEST_F_FORK(layout1, link)
16801686
16811687 ASSERT_EQ (-1 , link (file2_s1d1 , file1_s1d1 ));
16821688 ASSERT_EQ (EACCES , errno );
1689+
16831690 /* Denies linking because of reparenting. */
16841691 ASSERT_EQ (-1 , link (file1_s2d1 , file1_s1d2 ));
16851692 ASSERT_EQ (EXDEV , errno );
16861693 ASSERT_EQ (-1 , link (file2_s1d2 , file1_s1d3 ));
16871694 ASSERT_EQ (EXDEV , errno );
1695+ ASSERT_EQ (-1 , link (file2_s1d3 , file1_s1d2 ));
1696+ ASSERT_EQ (EXDEV , errno );
16881697
16891698 ASSERT_EQ (0 , link (file2_s1d2 , file1_s1d2 ));
16901699 ASSERT_EQ (0 , link (file2_s1d3 , file1_s1d3 ));
1700+
1701+ /* Prepares for next unlinks. */
1702+ ASSERT_EQ (0 , unlink (file2_s1d2 ));
1703+ ASSERT_EQ (0 , unlink (file2_s1d3 ));
1704+
1705+ ruleset_fd = create_ruleset (_metadata , layer2 [0 ].access , layer2 );
1706+ ASSERT_LE (0 , ruleset_fd );
1707+ enforce_ruleset (_metadata , ruleset_fd );
1708+ ASSERT_EQ (0 , close (ruleset_fd ));
1709+
1710+ /* Checks that linkind doesn't require the ability to delete a file. */
1711+ ASSERT_EQ (0 , link (file1_s1d2 , file2_s1d2 ));
1712+ ASSERT_EQ (0 , link (file1_s1d3 , file2_s1d3 ));
16911713}
16921714
16931715TEST_F_FORK (layout1 , rename_file )
@@ -1708,7 +1730,6 @@ TEST_F_FORK(layout1, rename_file)
17081730
17091731 ASSERT_LE (0 , ruleset_fd );
17101732
1711- ASSERT_EQ (0 , unlink (file1_s1d1 ));
17121733 ASSERT_EQ (0 , unlink (file1_s1d2 ));
17131734
17141735 enforce_ruleset (_metadata , ruleset_fd );
@@ -1744,9 +1765,15 @@ TEST_F_FORK(layout1, rename_file)
17441765 ASSERT_EQ (-1 , renameat2 (AT_FDCWD , dir_s2d2 , AT_FDCWD , file1_s2d1 ,
17451766 RENAME_EXCHANGE ));
17461767 ASSERT_EQ (EACCES , errno );
1768+ /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
1769+ ASSERT_EQ (-1 , rename (dir_s2d2 , file1_s2d1 ));
1770+ ASSERT_EQ (EACCES , errno );
17471771 ASSERT_EQ (-1 , renameat2 (AT_FDCWD , file1_s2d1 , AT_FDCWD , dir_s2d2 ,
17481772 RENAME_EXCHANGE ));
17491773 ASSERT_EQ (EACCES , errno );
1774+ /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
1775+ ASSERT_EQ (-1 , rename (file1_s1d1 , dir_s1d2 ));
1776+ ASSERT_EQ (EACCES , errno );
17501777
17511778 /* Renames files with different parents. */
17521779 ASSERT_EQ (-1 , rename (file1_s2d2 , file1_s1d2 ));
@@ -1809,9 +1836,15 @@ TEST_F_FORK(layout1, rename_dir)
18091836 ASSERT_EQ (-1 , renameat2 (AT_FDCWD , dir_s1d1 , AT_FDCWD , dir_s2d1 ,
18101837 RENAME_EXCHANGE ));
18111838 ASSERT_EQ (EACCES , errno );
1839+ /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
1840+ ASSERT_EQ (-1 , rename (dir_s1d2 , file1_s1d1 ));
1841+ ASSERT_EQ (EACCES , errno );
18121842 ASSERT_EQ (-1 , renameat2 (AT_FDCWD , file1_s1d1 , AT_FDCWD , dir_s1d2 ,
18131843 RENAME_EXCHANGE ));
18141844 ASSERT_EQ (EACCES , errno );
1845+ /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
1846+ ASSERT_EQ (-1 , rename (file1_s1d1 , dir_s1d2 ));
1847+ ASSERT_EQ (EACCES , errno );
18151848
18161849 /*
18171850 * Exchanges and renames directory to the same parent, which allows
0 commit comments