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

systemd / systemd / 21846209963

09 Feb 2026 03:52PM UTC coverage: 72.697% (-0.02%) from 72.716%
21846209963

push

github

daandemeyer
meson: guard symlinks in sysconfdir behind install_sysconfidr

Symlinks to files inside sysconfdir are now only installed if
ìnstall_sysconfdir=true (which is the default).

If sshconfdir,sshdconfdir,shellprofiledir are not inside sysconfdir and
install_sysconfidr=false, these symlinks are still installed to the
configured directory.

311951 of 429113 relevant lines covered (72.7%)

1156102.48 hits per line

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

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

3
#include <fcntl.h>
4
#include <signal.h>
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <string.h>
8
#include <sys/file.h>
9
#include <unistd.h>
10

11
#include "alloc-util.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "fs-util.h"
15
#include "lock-util.h"
16
#include "log.h"
17
#include "path-util.h"
18
#include "pidref.h"
19
#include "process-util.h"
20
#include "string-util.h"
21
#include "time-util.h"
22

23
int make_lock_file_at(int dir_fd, const char *p, int operation, LockFile *ret) {
217,929✔
24
        _cleanup_close_ int fd = -EBADF, dfd = -EBADF;
217,929✔
25
        _cleanup_free_ char *t = NULL;
217,929✔
26

27
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
217,929✔
28
        assert(p);
217,929✔
29
        assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
217,929✔
30
        assert(ret);
217,929✔
31

32
        if (isempty(p))
435,858✔
33
                return -EINVAL;
34

35
        /* We use UNPOSIX locks as they have nice semantics, and are mostly compatible with NFS. */
36

37
        dfd = fd_reopen(dir_fd, O_CLOEXEC|O_PATH|O_DIRECTORY);
217,929✔
38
        if (dfd < 0)
217,929✔
39
                return dfd;
40

41
        t = strdup(p);
217,929✔
42
        if (!t)
217,929✔
43
                return -ENOMEM;
44

45
        fd = xopenat_lock_full(dfd,
217,929✔
46
                               p,
47
                               O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY,
48
                               /* xopen_flags= */ 0,
49
                               0600,
50
                               LOCK_UNPOSIX,
51
                               operation);
52
        if (fd < 0)
217,929✔
53
                return fd == -EAGAIN ? -EBUSY : fd;
3✔
54

55
        *ret = (LockFile) {
217,926✔
56
                .dir_fd = TAKE_FD(dfd),
217,926✔
57
                .path = TAKE_PTR(t),
217,926✔
58
                .fd = TAKE_FD(fd),
217,926✔
59
                .operation = operation,
60
        };
61

62
        return 0;
217,926✔
63
}
64

65
int make_lock_file_for(const char *p, int operation, LockFile *ret) {
815✔
66
        _cleanup_free_ char *fn = NULL, *dn = NULL, *t = NULL;
815✔
67
        int r;
815✔
68

69
        assert(p);
815✔
70
        assert(ret);
815✔
71

72
        r = path_split_prefix_filename(p, &dn, &fn);
815✔
73
        if (r < 0)
815✔
74
                return r;
75

76
        if (dn)
815✔
77
                t = strjoin(dn, "/.#", fn, ".lck");
815✔
78
        else
79
                t = strjoin(".#", fn, ".lck");
×
80
        if (!t)
815✔
81
                return -ENOMEM;
82

83
        return make_lock_file(t, operation, ret);
815✔
84
}
85

86
void release_lock_file(LockFile *f) {
434,855✔
87
        if (!f)
434,855✔
88
                return;
89

90
        if (f->path) {
434,855✔
91

92
                /* If we are the exclusive owner we can safely delete
93
                 * the lock file itself. If we are not the exclusive
94
                 * owner, we can try becoming it. */
95

96
                if (f->fd >= 0 &&
217,459✔
97
                    (f->operation & ~LOCK_NB) == LOCK_SH &&
218,175✔
98
                    unposix_lock(f->fd, LOCK_EX|LOCK_NB) >= 0)
716✔
99
                        f->operation = LOCK_EX|LOCK_NB;
715✔
100

101
                if ((f->operation & ~LOCK_NB) == LOCK_EX)
217,459✔
102
                        (void) unlinkat(f->dir_fd, f->path, 0);
217,458✔
103

104
                f->path = mfree(f->path);
217,459✔
105
        }
106

107
        f->dir_fd = safe_close(f->dir_fd);
434,855✔
108
        f->fd = safe_close(f->fd);
434,855✔
109
        f->operation = 0;
434,855✔
110
}
111

112
static int fcntl_lock(int fd, int operation, bool ofd) {
233,089✔
113
        int cmd, type, r;
233,089✔
114

115
        assert(fd >= 0);
233,089✔
116

117
        if (ofd)
233,089✔
118
                cmd = (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW;
232,698✔
119
        else
120
                cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW;
391✔
121

122
        switch (operation & ~LOCK_NB) {
233,089✔
123
                case LOCK_EX:
124
                        type = F_WRLCK;
125
                        break;
126
                case LOCK_SH:
756✔
127
                        type = F_RDLCK;
756✔
128
                        break;
756✔
129
                case LOCK_UN:
195✔
130
                        type = F_UNLCK;
195✔
131
                        break;
195✔
132
                default:
×
133
                        assert_not_reached();
×
134
        }
135

136
        r = RET_NERRNO(fcntl(fd, cmd, &(struct flock) {
233,089✔
137
                .l_type = type,
138
                .l_whence = SEEK_SET,
139
                .l_start = 0,
140
                .l_len = 0,
141
        }));
142

143
        /* If we are doing non-blocking operations, treat EACCES/EAGAIN the same as per man page. But if
144
         * not, propagate EACCES back, as it will likely be due to an LSM denying the operation (for example
145
         * LXC with AppArmor when running on kernel < 6.2), and in some cases we want to gracefully
146
         * fallback (e.g.: PrivateNetwork=yes). As per documentation, it's only the non-blocking operation
147
         * F_SETLK that might return EACCES on some platforms (although the Linux implementation doesn't
148
         * seem to), as F_SETLKW and F_OFD_SETLKW block so this is not an issue, and F_OFD_SETLK is documented
149
         * to only return EAGAIN if the lock is already held. */
150
        if ((operation & LOCK_NB) && r == -EACCES)
233,089✔
151
                r = -EAGAIN;
×
152

153
        return r;
233,089✔
154
}
155

156
int posix_lock(int fd, int operation) {
239✔
157
        return fcntl_lock(fd, operation, /* ofd= */ false);
239✔
158
}
159

160
int unposix_lock(int fd, int operation) {
232,698✔
161
        return fcntl_lock(fd, operation, /* ofd= */ true);
232,698✔
162
}
163

164
void posix_unlockpp(int **fd) {
152✔
165
        assert(fd);
152✔
166

167
        if (!*fd || **fd < 0)
152✔
168
                return;
169

170
        (void) fcntl_lock(**fd, LOCK_UN, /* ofd= */ false);
152✔
171
        *fd = NULL;
152✔
172
}
173

174
void unposix_unlockpp(int **fd) {
×
175
        assert(fd);
×
176

177
        if (!*fd || **fd < 0)
×
178
                return;
179

180
        (void) fcntl_lock(**fd, LOCK_UN, /* ofd= */ true);
×
181
        *fd = NULL;
×
182
}
183

184
int lock_generic(int fd, LockType type, int operation) {
270,400✔
185
        assert(fd >= 0);
270,400✔
186

187
        switch (type) {
270,400✔
188
        case LOCK_NONE:
189
                return 0;
190
        case LOCK_BSD:
2,941✔
191
                return RET_NERRNO(flock(fd, operation));
2,941✔
192
        case LOCK_POSIX:
1✔
193
                return posix_lock(fd, operation);
1✔
194
        case LOCK_UNPOSIX:
231,683✔
195
                return unposix_lock(fd, operation);
231,683✔
196
        default:
×
197
                assert_not_reached();
×
198
        }
199
}
200

201
int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout) {
486✔
202
        int r;
486✔
203

204
        assert(fd >= 0);
486✔
205

206
        /* A version of lock_generic(), but with a timeout. We do this in a child process, since the kernel
207
         * APIs natively don't support a timeout. We set a SIGALRM timer that will kill the child after the
208
         * timeout is hit. Returns -ETIMEDOUT if the timeout is hit, and 0 on success.
209
         *
210
         * This only works for BSD and UNPOSIX locks, as only those are fd-bound, and hence can be acquired
211
         * from any process that has access to the fd. POSIX locks OTOH are process-bound, and hence if we'd
212
         * acquire them in a child process they'd remain unlocked in the parent. */
213

214
        if (type == LOCK_NONE)
486✔
215
                return 0;
486✔
216
        if (!IN_SET(type, LOCK_BSD, LOCK_UNPOSIX)) /* Not for POSIX locks, see above. */
486✔
217
                return -EOPNOTSUPP;
218

219
        /* First, try without forking anything off */
220
        r = lock_generic(fd, type, operation | (timeout == USEC_INFINITY ? 0 : LOCK_NB));
972✔
221
        if (r != -EAGAIN || timeout == 0 || FLAGS_SET(operation, LOCK_NB))
486✔
222
                return r;
223

224
        /* If that didn't work, try with a child */
225
        _cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
465✔
226
        r = pidref_safe_fork("(sd-flock)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pidref);
465✔
227
        if (r < 0)
928✔
228
                return log_error_errno(r, "Failed to flock block device in child process: %m");
×
229
        if (r == 0) {
928✔
230
                struct sigevent sev = {
463✔
231
                        .sigev_notify = SIGEV_SIGNAL,
232
                        .sigev_signo = SIGALRM,
233
                };
234
                timer_t id;
463✔
235

236
                if (timer_create(CLOCK_MONOTONIC, &sev, &id) < 0) {
463✔
237
                        log_error_errno(errno, "Failed to allocate CLOCK_MONOTONIC timer: %m");
×
238
                        _exit(EXIT_FAILURE);
×
239
                }
240

241
                struct itimerspec its = {};
463✔
242
                timespec_store(&its.it_value, timeout);
463✔
243

244
                if (timer_settime(id, /* flags= */ 0, &its, NULL) < 0) {
463✔
245
                        log_error_errno(errno, "Failed to start CLOCK_MONOTONIC timer: %m");
×
246
                        _exit(EXIT_FAILURE);
×
247
                }
248

249
                if (lock_generic(fd, type, operation) < 0) {
463✔
250
                        log_error_errno(errno, "Unable to get an exclusive lock on the device: %m");
×
251
                        _exit(EXIT_FAILURE);
×
252
                }
253

254
                _exit(EXIT_SUCCESS);
463✔
255
        }
256

257
        siginfo_t status;
465✔
258
        r = pidref_wait_for_terminate(&pidref, &status);
465✔
259
        if (r < 0)
465✔
260
                return r;
261

262
        pidref_done(&pidref);
465✔
263

264
        switch (status.si_code) {
465✔
265

266
        case CLD_EXITED:
463✔
267
                if (status.si_status != EXIT_SUCCESS)
463✔
268
                        return -EPROTO;
×
269

270
                return 0;
271

272
        case CLD_KILLED:
2✔
273
                if (status.si_status == SIGALRM)
2✔
274
                        return -ETIMEDOUT;
2✔
275

276
                _fallthrough_;
277

278
        case CLD_DUMPED:
279
                return -EPROTO;
280

281
        default:
×
282
                assert_not_reached();
×
283
        }
284
}
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