1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved.
4 * Author: Yang Xu <xuyang2018.jy@fujitsu.com>
5 * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
6 */
7
8 /*\
9 * [Description]
10 *
11 * Verify that unlink(2) fails with EPERM when target file is marked as
12 * immutable or append-only.
13 */
14
15 #include <sys/ioctl.h>
16 #include "tst_test.h"
17 #include "lapi/fs.h"
18
19 #define MNTPOINT "mnt"
20 #define TEST_EPERM_IMMUTABLE MNTPOINT"/test_eperm_immutable"
21 #define TEST_EPERM_APPEND_ONLY MNTPOINT"/test_eperm_append_only"
22
23 static int fd_immutable = -1;
24 static int fd_append_only = -1;
25
26 static struct test_case_t {
27 char *filename;
28 int *fd;
29 int flag;
30 char *desc;
31 } tcases[] = {
32 {TEST_EPERM_IMMUTABLE, &fd_immutable, FS_IMMUTABLE_FL,
33 "target file is immutable"},
34 {TEST_EPERM_APPEND_ONLY, &fd_append_only, FS_APPEND_FL,
35 "target file is append-only"},
36 };
37
setup_inode_flag(const int fd,const int flag,const int set)38 static void setup_inode_flag(const int fd, const int flag, const int set)
39 {
40 int attr;
41
42 SAFE_IOCTL(fd, FS_IOC_GETFLAGS, &attr);
43
44 if (set)
45 attr |= flag;
46 else
47 attr &= ~flag;
48
49 SAFE_IOCTL(fd, FS_IOC_SETFLAGS, &attr);
50 }
51
setup(void)52 static void setup(void)
53 {
54 int attr;
55
56 /* inode attributes in tmpfs are supported from kernel 6.0
57 * https://lore.kernel.org/all/20220715015912.2560575-1-tytso@mit.edu/
58 */
59 if (!strcmp(tst_device->fs_type, "tmpfs") && tst_kvercmp(6, 0, 0) < 0)
60 tst_brk(TCONF, "FS_IOC_GETFLAGS on tmpfs not supported for kernel<6.0");
61
62 fd_immutable = SAFE_CREAT(TEST_EPERM_IMMUTABLE, 0600);
63 TEST(ioctl(fd_immutable, FS_IOC_GETFLAGS, &attr));
64
65 if (TST_RET == -1 && TST_ERR == ENOTTY) {
66 SAFE_CLOSE(fd_immutable);
67
68 tst_brk(TBROK, "Inode attributes not supported by '%s'",
69 tst_device->fs_type);
70 }
71
72 attr |= FS_IMMUTABLE_FL;
73 SAFE_IOCTL(fd_immutable, FS_IOC_SETFLAGS, &attr);
74
75 fd_append_only = SAFE_CREAT(TEST_EPERM_APPEND_ONLY, 0600);
76 setup_inode_flag(fd_append_only, FS_APPEND_FL, 1);
77 }
78
cleanup(void)79 static void cleanup(void)
80 {
81 if (fd_immutable != -1) {
82 setup_inode_flag(fd_immutable, FS_IMMUTABLE_FL, 0);
83 SAFE_CLOSE(fd_immutable);
84 }
85
86 if (fd_append_only != -1) {
87 setup_inode_flag(fd_append_only, FS_APPEND_FL, 0);
88 SAFE_CLOSE(fd_append_only);
89 }
90 }
91
verify_unlink(unsigned int i)92 static void verify_unlink(unsigned int i)
93 {
94 struct test_case_t *tc = &tcases[i];
95
96 TST_EXP_FAIL(unlink(tc->filename), EPERM, "%s", tc->desc);
97
98 /* If unlink() succeeded unexpectedly, test file should be restored. */
99 if (!TST_RET) {
100 *(tc->fd) = SAFE_CREAT(tc->filename, 0600);
101 setup_inode_flag(*(tc->fd), tc->flag, 1);
102 }
103 }
104
105 static struct tst_test test = {
106 .setup = setup,
107 .tcnt = ARRAY_SIZE(tcases),
108 .cleanup = cleanup,
109 .test = verify_unlink,
110 .mntpoint = MNTPOINT,
111 .needs_root = 1,
112 .mount_device = 1,
113 .all_filesystems = 1,
114 .skip_filesystems = (const char *const[]) {
115 "fuse",
116 "exfat",
117 "vfat",
118 "ntfs",
119 NULL
120 },
121 };
122