• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 16280725298

14 Jul 2025 08:16PM UTC coverage: 72.166% (-0.006%) from 72.172%
16280725298

push

github

web-flow
Two fixlets for coverage test (#38183)

302135 of 418667 relevant lines covered (72.17%)

773261.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.81
/src/basic/chattr-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <sys/ioctl.h>
5
#include <sys/stat.h>
6

7
#include "bitfield.h"
8
#include "chattr-util.h"
9
#include "errno-util.h"
10
#include "fd-util.h"
11
#include "fs-util.h"
12
#include "log.h"
13
#include "recurse-dir.h"
14
#include "string-util.h"
15

16
int chattr_full(
1,197✔
17
              int dir_fd,
18
              const char *path,
19
              unsigned value,
20
              unsigned mask,
21
              unsigned *ret_previous,
22
              unsigned *ret_final,
23
              ChattrApplyFlags flags) {
24

25
        _cleanup_close_ int fd = -EBADF;
1,197✔
26
        unsigned old_attr, new_attr;
1,197✔
27
        int set_flags_errno = 0;
1,197✔
28
        struct stat st;
1,197✔
29

30
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
1,197✔
31

32
        fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
1,197✔
33
        if (fd < 0)
1,197✔
34
                return fd;
35

36
        if (fstat(fd, &st) < 0)
1,190✔
37
                return -errno;
×
38

39
        /* Explicitly check whether this is a regular file or directory. If it is anything else (such
40
         * as a device node or fifo), then the ioctl will not hit the file systems but possibly
41
         * drivers, where the ioctl might have different effects. Notably, DRM is using the same
42
         * ioctl() number. */
43

44
        if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
1,190✔
45
                return -ENOTTY;
46

47
        if (mask == 0 && !ret_previous && !ret_final)
1,190✔
48
                return 0;
49

50
        if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
1,190✔
51
                return -errno;
×
52

53
        new_attr = (old_attr & ~mask) | (value & mask);
1,190✔
54
        if (new_attr == old_attr) {
1,190✔
55
                if (ret_previous)
241✔
56
                        *ret_previous = old_attr;
×
57
                if (ret_final)
241✔
58
                        *ret_final = old_attr;
×
59
                return 0;
241✔
60
        }
61

62
        if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) >= 0) {
949✔
63
                unsigned attr;
17✔
64

65
                /* Some filesystems (BTRFS) silently fail when a flag cannot be set. Let's make sure our
66
                 * changes actually went through by querying the flags again and verifying they're equal to
67
                 * the flags we tried to configure. */
68

69
                if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
17✔
70
                        return -errno;
17✔
71

72
                if (new_attr == attr) {
17✔
73
                        if (ret_previous)
17✔
74
                                *ret_previous = old_attr;
2✔
75
                        if (ret_final)
17✔
76
                                *ret_final = new_attr;
×
77
                        return 1;
17✔
78
                }
79

80
                /* Trigger the fallback logic. */
81
                errno = EINVAL;
×
82
        }
83

84
        if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno) || !FLAGS_SET(flags, CHATTR_FALLBACK_BITWISE))
932✔
85
                return -errno;
875✔
86

87
        /* When -EINVAL is returned, incompatible attributes might be simultaneously specified. E.g.,
88
         * compress(c) and nocow(C) attributes cannot be set to files on btrfs. As a fallback, let's try to
89
         * set attributes one by one.
90
         *
91
         * Alternatively, when we get EINVAL or EOPNOTSUPP (or a similar error code) we assume a flag might
92
         * just not be supported, and we can ignore it too */
93

94
        unsigned current_attr = old_attr;
57✔
95

96
        BIT_FOREACH(i, mask) {
141✔
97
                unsigned new_one, mask_one = 1u << i;
84✔
98

99
                new_one = UPDATE_FLAG(current_attr, mask_one, FLAGS_SET(value, mask_one));
84✔
100
                if (new_one == current_attr)
84✔
101
                        continue;
59✔
102

103
                if (ioctl(fd, FS_IOC_SETFLAGS, &new_one) < 0) {
84✔
104
                        if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
59✔
105
                                return -errno;
×
106

107
                        log_full_errno(FLAGS_SET(flags, CHATTR_WARN_UNSUPPORTED_FLAGS) ? LOG_WARNING : LOG_DEBUG,
116✔
108
                                       errno,
109
                                       "Unable to set file attribute 0x%x on %s, ignoring: %m", mask_one, strna(path));
110

111
                        /* Ensures that we record whether only EOPNOTSUPP&friends are encountered, or if a more serious
112
                         * error (thus worth logging at a different level, etc) was seen too. */
113
                        if (set_flags_errno == 0 || !ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
59✔
114
                                set_flags_errno = -errno;
57✔
115

116
                        continue;
59✔
117
                }
118

119
                if (ioctl(fd, FS_IOC_GETFLAGS, &current_attr) < 0)
25✔
120
                        return -errno;
×
121
        }
122

123
        if (ret_previous)
57✔
124
                *ret_previous = old_attr;
48✔
125
        if (ret_final)
57✔
126
                *ret_final = current_attr;
48✔
127

128
        /* -ENOANO indicates that some attributes cannot be set. ERRNO_IS_IOCTL_NOT_SUPPORTED indicates that
129
         * all encountered failures were due to flags not supported by the FS, so return a specific error in
130
         * that case, so callers can handle it properly (e.g.: tmpfiles.d can use debug level logging). */
131
        return current_attr == new_attr ? 1 : ERRNO_IS_IOCTL_NOT_SUPPORTED(set_flags_errno) ? set_flags_errno : -ENOANO;
57✔
132
}
133

134
int read_attr_fd(int fd, unsigned *ret) {
402,555✔
135
        struct stat st;
402,555✔
136

137
        assert(fd >= 0);
402,555✔
138
        assert(ret);
402,555✔
139

140
        if (fstat(fd, &st) < 0)
402,555✔
141
                return -errno;
×
142

143
        if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
402,555✔
144
                return -ENOTTY;
145

146
        _cleanup_close_ int fd_close = -EBADF;
402,555✔
147
        fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY, O_PATH, &fd_close); /* drop O_PATH if it is set */
402,551✔
148
        if (fd < 0)
402,551✔
149
                return fd;
150

151
        return RET_NERRNO(ioctl(fd, FS_IOC_GETFLAGS, ret));
600,651✔
152
}
153

154
int read_attr_at(int dir_fd, const char *path, unsigned *ret) {
398,742✔
155
        _cleanup_close_ int fd_close = -EBADF;
398,742✔
156
        int fd;
398,742✔
157

158
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
398,742✔
159
        assert(ret);
398,742✔
160

161
        if (isempty(path) && dir_fd != AT_FDCWD)
398,742✔
162
                fd = dir_fd;
163
        else {
164
                fd_close = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
1,587✔
165
                if (fd_close < 0)
1,587✔
166
                        return fd_close;
167

168
                fd = fd_close;
169
        }
170

171
        return read_attr_fd(fd, ret);
398,492✔
172
}
173

174
int read_fs_xattr_fd(int fd, uint32_t *ret_xflags, uint32_t *ret_projid) {
1,465✔
175
        struct fsxattr attrs;
1,465✔
176
        _cleanup_close_ int fd_reopened = -EBADF;
1,465✔
177

178
        assert(fd >= 0);
1,465✔
179

180
        fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY, O_PATH, &fd_reopened);
1,465✔
181
        if (fd < 0)
1,465✔
182
                return fd;
183

184
        if (ioctl(fd, FS_IOC_FSGETXATTR, &attrs) < 0)
1,465✔
185
                return -errno;
×
186

187
        if (ret_xflags)
1,465✔
188
                *ret_xflags = attrs.fsx_xflags;
×
189

190
        if (ret_projid)
1,465✔
191
                *ret_projid = attrs.fsx_projid;
1,465✔
192

193
        return 0;
194
}
195

196
int set_proj_id(int fd, uint32_t proj_id) {
×
197
        struct fsxattr attrs;
×
198
        _cleanup_close_ int fd_reopened = -EBADF;
×
199

200
        assert(fd >= 0);
×
201

202
        fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY, O_PATH, &fd_reopened);
×
203
        if (fd < 0)
×
204
                return fd;
205

206
        if (ioctl(fd, FS_IOC_FSGETXATTR, &attrs) < 0)
×
207
                return -errno;
×
208

209
        struct stat statbuf;
×
210
        if (fstat(fd, &statbuf) < 0)
×
211
                return -errno;
×
212

213
        if (attrs.fsx_projid == proj_id && (!S_ISDIR(statbuf.st_mode) || FLAGS_SET(attrs.fsx_xflags, FS_XFLAG_PROJINHERIT)))
×
214
                return 0;
215

216
        attrs.fsx_projid = proj_id;
×
217
        if (S_ISDIR(statbuf.st_mode))
×
218
                attrs.fsx_xflags |= FS_XFLAG_PROJINHERIT;
×
219

220
        return RET_NERRNO(ioctl(fd, FS_IOC_FSSETXATTR, &attrs));
×
221
}
222

223
static int set_proj_id_cb(
×
224
                RecurseDirEvent event,
225
                const char *path,
226
                int dir_fd,
227
                int inode_fd,
228
                const struct dirent *de,
229
                const struct statx *sx,
230
                void *userdata) {
231

232
        if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
×
233
                return RECURSE_DIR_CONTINUE;
234

235
        if (de && !IN_SET(de->d_type, DT_DIR, DT_REG))
×
236
                return RECURSE_DIR_CONTINUE;
237

238
        return set_proj_id(inode_fd, PTR_TO_UINT32(userdata));
×
239
}
240

241
int set_proj_id_recursive(int fd, uint32_t proj_id) {
×
242
        return recurse_dir_at(
×
243
                        fd,
244
                        /* path = */ NULL,
245
                        /* statx_mask = */ 0,
246
                        /* n_depth_max = */ UINT_MAX,
247
                        RECURSE_DIR_ENSURE_TYPE|RECURSE_DIR_TOPLEVEL|RECURSE_DIR_INODE_FD,
248
                        set_proj_id_cb,
249
                        UINT32_TO_PTR(proj_id));
×
250
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc