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

systemd / systemd / 15232239991

24 May 2025 08:01PM UTC coverage: 72.053% (-0.02%) from 72.07%
15232239991

push

github

web-flow
docs: add man pages for sd_device_enumerator_[new,ref,unref,unrefp] (#37586)

For #20929.

299160 of 415197 relevant lines covered (72.05%)

703671.29 hits per line

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

88.61
/src/shared/data-fd-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/stat.h>
4
#include <unistd.h>
5

6
#include "copy.h"
7
#include "data-fd-util.h"
8
#include "fd-util.h"
9
#include "fs-util.h"
10
#include "memfd-util.h"
11
#include "missing_mman.h"
12
#include "tmpfile-util.h"
13

14
/* When the data is smaller or equal to 64K, try to place the copy in a memfd */
15
#define DATA_FD_MEMORY_LIMIT (64U * U64_KB)
16

17
/* If memfd didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp/ instead. */
18
#define DATA_FD_TMP_LIMIT (1U * U64_MB)
19

20
int copy_data_fd(int fd) {
95✔
21
        _cleanup_close_ int copy_fd = -EBADF, tmp_fd = -EBADF;
95✔
22
        const char *td;
95✔
23
        struct stat st;
95✔
24
        int r;
95✔
25

26
        /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only
27
         * fashion, but independent of it (i.e. the source fd can be closed and unmounted after this call
28
         * succeeded). Tries to be somewhat smart about where to place the data. In the best case uses a
29
         * memfd(). For larger data will use an unlinked file in /tmp/, and for even larger data one in
30
         * /var/tmp/. */
31

32
        if (fstat(fd, &st) < 0)
95✔
33
                return -errno;
×
34

35
        /* For now, let's only accept regular files, sockets, pipes and char devices */
36
        if (S_ISDIR(st.st_mode))
95✔
37
                return -EISDIR;
38
        if (S_ISLNK(st.st_mode))
95✔
39
                return -ELOOP;
40
        if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
95✔
41
                return -EBADFD;
42

43
        /* If we have reason to believe the data is bounded in size, then let's use memfds as backing
44
         * fd. Note that we use the reported regular file size only as a hint, given that there are plenty
45
         * special files in /proc/ and /sys/ which report a zero file size but can be read from. */
46

47
        if (!S_ISREG(st.st_mode) || (uint64_t) st.st_size < DATA_FD_MEMORY_LIMIT) {
95✔
48

49
                /* Try a memfd first */
50
                copy_fd = memfd_new_full("data-fd", MFD_ALLOW_SEALING);
95✔
51
                if (copy_fd < 0)
95✔
52
                        return copy_fd;
53

54
                r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, COPY_REFLINK);
95✔
55
                if (r < 0)
95✔
56
                        return r;
57

58
                off_t f = lseek(copy_fd, 0, SEEK_SET);
95✔
59
                if (f < 0)
95✔
60
                        return -errno;
×
61
                if (f != 0)
95✔
62
                        return -EIO;
63

64
                if (r == 0) {
95✔
65
                        /* Did it fit into the limit? If so, we are done. */
66
                        r = memfd_set_sealed(copy_fd);
94✔
67
                        if (r < 0)
94✔
68
                                return r;
69

70
                        return TAKE_FD(copy_fd);
94✔
71
                }
72
        }
73

74
        /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
75
        if ((!S_ISREG(st.st_mode) || (uint64_t) st.st_size < DATA_FD_TMP_LIMIT)) {
1✔
76
                tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
1✔
77
                if (tmp_fd < 0)
1✔
78
                        return tmp_fd;
79

80
                if (copy_fd >= 0) {
1✔
81
                        /* If we tried a memfd first and it ended up being too large, then copy this into the
82
                         * temporary file first. */
83

84
                        r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
1✔
85
                        if (r < 0)
1✔
86
                                return r;
87

88
                        assert(r == 0);
1✔
89
                }
90

91
                r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT, COPY_REFLINK);
1✔
92
                if (r < 0)
1✔
93
                        return r;
94
                if (r == 0)
1✔
95
                        goto finish;  /* Yay, it fit in */
×
96

97
                /* It didn't fit in. Let's not forget to use what we already used */
98
                off_t f = lseek(tmp_fd, 0, SEEK_SET);
1✔
99
                if (f < 0)
1✔
100
                        return -errno;
×
101
                if (f != 0)
1✔
102
                        return -EIO;
103

104
                close_and_replace(copy_fd, tmp_fd);
1✔
105
        }
106

107
        /* As last fallback use /var/tmp/ */
108
        r = var_tmp_dir(&td);
1✔
109
        if (r < 0)
1✔
110
                return r;
111

112
        tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
1✔
113
        if (tmp_fd < 0)
1✔
114
                return tmp_fd;
115

116
        if (copy_fd >= 0) {
1✔
117
                /* If we tried a memfd first, or a file in /tmp/, and it ended up being too large, than copy this
118
                 * into the temporary file first. */
119
                r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
1✔
120
                if (r < 0)
1✔
121
                        return r;
122

123
                assert(r == 0);
1✔
124
        }
125

126
        /* Copy in the rest */
127
        r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
1✔
128
        if (r < 0)
1✔
129
                return r;
130

131
        assert(r == 0);
1✔
132

133
finish:
1✔
134
        /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
135
         * file again */
136

137
        return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
1✔
138
}
139

140
int memfd_clone_fd(int fd, const char *name, int mode) {
1✔
141
        _cleanup_close_ int mfd = -EBADF;
1✔
142
        struct stat st;
1✔
143
        bool ro, exec;
1✔
144
        int r;
1✔
145

146
        /* Creates a clone of a regular file in a memfd. Unlike copy_data_fd() this returns strictly a memfd
147
         * (and if it can't it will fail). Thus the resulting fd is seekable, and definitely reports as
148
         * S_ISREG. */
149

150
        assert(fd >= 0);
1✔
151
        assert(name);
1✔
152
        assert(IN_SET(mode & O_ACCMODE_STRICT, O_RDONLY, O_RDWR));
1✔
153
        assert((mode & ~(O_RDONLY|O_RDWR|O_CLOEXEC)) == 0);
1✔
154

155
        if (fstat(fd, &st) < 0)
1✔
156
                return -errno;
×
157

158
        ro = (mode & O_ACCMODE_STRICT) == O_RDONLY;
1✔
159
        exec = st.st_mode & 0111;
1✔
160

161
        mfd = memfd_create_wrapper(name,
1✔
162
                                   ((FLAGS_SET(mode, O_CLOEXEC) || ro) ? MFD_CLOEXEC : 0) |
1✔
163
                                   (ro ? MFD_ALLOW_SEALING : 0) |
1✔
164
                                   (exec ? MFD_EXEC : MFD_NOEXEC_SEAL));
1✔
165
        if (mfd < 0)
1✔
166
                return mfd;
167

168
        r = copy_bytes(fd, mfd, UINT64_MAX, COPY_REFLINK);
1✔
169
        if (r < 0)
1✔
170
                return r;
171

172
        if (ro) {
1✔
173
                r = memfd_set_sealed(mfd);
×
174
                if (r < 0)
×
175
                        return r;
176

177
                return fd_reopen(mfd, mode);
×
178
        }
179

180
        off_t f = lseek(mfd, 0, SEEK_SET);
1✔
181
        if (f < 0)
1✔
182
                return -errno;
×
183

184
        return TAKE_FD(mfd);
185
}
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