Coverage Report

Created: 2025-11-24 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dbus-broker/src/util/fdlist.c
Line
Count
Source
1
/*
2
 * File-Descriptor List
3
 *
4
 * The FDList object is a small wrapper around a fixed-size array of
5
 * file-descriptors. It allows easy handling of file-descriptor sets as atomic
6
 * entity, while still providing access to individual entries.
7
 *
8
 * Furthermore, the FDList object is meant as supplement for AF_UNIX sockets.
9
 * Hence, it stores FDs as a cmsghdr entry, ready to be used with sendmsg(2).
10
 */
11
12
#include <c-stdaux.h>
13
#include <stdlib.h>
14
#include <sys/socket.h>
15
#include "util/error.h"
16
#include "util/fdlist.h"
17
18
/**
19
 * fdlist_new_with_fds() - create fdlist with a set of FDs
20
 * @listp:              output for new fdlist
21
 * @fds:                FD array to import
22
 * @n_fds:              array size of @fds
23
 *
24
 * This creates a new fdlist and imports the FDs given as @fds + @n_fds. The
25
 * FDs are still owned by the caller and will not be freed on fdlist_free().
26
 *
27
 * Return: 0 on success, negative error code on failure.
28
 */
29
0
int fdlist_new_with_fds(FDList **listp, const int *fds, size_t n_fds) {
30
0
        FDList *list;
31
32
0
        list = calloc(1, sizeof(*list) + CMSG_SPACE(n_fds * sizeof(int)));
33
0
        if (!list)
34
0
                return error_origin(-ENOMEM);
35
36
0
        list->consumed = false;
37
0
        list->cmsg->cmsg_len = CMSG_LEN(n_fds * sizeof(int));
38
0
        list->cmsg->cmsg_level = SOL_SOCKET;
39
0
        list->cmsg->cmsg_type = SCM_RIGHTS;
40
0
        c_memcpy(fdlist_data(list), fds, n_fds * sizeof(int));
41
42
0
        *listp = list;
43
0
        return 0;
44
0
}
45
46
/**
47
 * fdlist_new_consume_fds() - create fdlist and consume FDs
48
 * @listp:              output for new fdlist
49
 * @fds:                FD array to import
50
 * @n_fds:              array size of @fds
51
 *
52
 * This is the same as fdlist_new_with_fds() but consumes the FDs. That is, the
53
 * caller no longer owns the FDs, and the FDs will be closed on fdlist_free().
54
 *
55
 * Return: 0 on success, negative error code on failure.
56
 */
57
0
int fdlist_new_consume_fds(FDList **listp, const int *fds, size_t n_fds) {
58
0
        int r;
59
60
0
        r = fdlist_new_with_fds(listp, fds, n_fds);
61
0
        if (!r)
62
0
                (*listp)->consumed = true;
63
64
0
        return r;
65
0
}
66
67
/**
68
 * fdlist_new_dup_fds() - create fdlist and duplicate FDs
69
 * @listp:              output for new fdlist
70
 * @fds:                FD array to duplicate
71
 * @n_fds:              array size of @fds
72
 *
73
 * This is the same as fdlist_new_with_fds() but duplicates the FDs. That is,
74
 * the caller retains the FDs, and an independent copy of each FD will be owned
75
 * by the new object.
76
 *
77
 * Return: 0 on success, negative error code on failure.
78
 */
79
0
int fdlist_new_dup_fds(FDList **listp, const int *fds, size_t n_fds) {
80
0
        _c_cleanup_(fdlist_freep) FDList *list = NULL;
81
0
        size_t i;
82
0
        int r, *p;
83
84
0
        r = fdlist_new_with_fds(&list, fds, n_fds);
85
0
        if (r)
86
0
                return error_trace(r);
87
88
0
        p = fdlist_data(list);
89
0
        for (i = 0; i < n_fds; ++i) {
90
0
                p[i] = fcntl(p[i], F_DUPFD_CLOEXEC, 3);
91
0
                if (p[i] < 0) {
92
0
                        r = -errno;
93
0
                        while (i > 0) {
94
0
                                --i;
95
0
                                p[i] = c_close(p[i]);
96
0
                        }
97
0
                        return error_origin(r);
98
0
                }
99
0
        }
100
101
0
        list->consumed = true;
102
103
0
        *listp = list;
104
0
        list = NULL;
105
0
        return 0;
106
0
}
107
108
/**
109
 * fdlist_free() - free fdlist
110
 * @list:               fdlist to operate on, or NULL
111
 *
112
 * This frees the fdlist given as @list. If the file-descriptors were marked as
113
 * `consumed`, this function will close them. Otherwise, they're left
114
 * untouched.
115
 *
116
 * If @list is NULL, this is a no-op.
117
 *
118
 * Return: NULL is returned.
119
 */
120
2.98k
FDList *fdlist_free(FDList *list) {
121
2.98k
        size_t i, n;
122
2.98k
        int *p;
123
124
2.98k
        if (list) {
125
0
                p = fdlist_data(list);
126
0
                n = fdlist_count(list);
127
128
0
                if (list->consumed)
129
0
                        for (i = 0; i < n; ++i)
130
0
                                c_close(p[i]);
131
132
0
                free(list);
133
0
        }
134
135
2.98k
        return NULL;
136
2.98k
}
137
138
/**
139
 * fdlist_truncate() - truncate fdlist
140
 * @list:               fdlist to operate on
141
 * @n_fds:              number of FDs to retain
142
 *
143
 * This shrinks the fdlist to size @n_fds. The caller must make sure the fdlist
144
 * is sized greater than, or equal to, @n_fds.
145
 *
146
 * If the discarded FDs were marked as consumed, then this will close them.
147
 * Otherwise, they're left untouched.
148
 */
149
0
void fdlist_truncate(FDList *list, size_t n_fds) {
150
0
        size_t i, n;
151
0
        int *p;
152
153
0
        p = fdlist_data(list);
154
0
        n = fdlist_count(list);
155
156
0
        c_assert(n_fds <= n);
157
158
0
        if (list->consumed)
159
0
                for (i = n_fds; i < n; ++i)
160
0
                        c_close(p[i]);
161
162
0
        list->cmsg->cmsg_len = CMSG_LEN(n_fds * sizeof(int));
163
0
}
164
165
/**
166
 * fdlist_steal() - steal FD
167
 * @list:               fdlist to operate on
168
 * @index:              index of FD
169
 *
170
 * This returns the FD at position @index and then drops it form the fdlist, by
171
 * replacing it with -1.
172
 *
173
 * If @index points outside the fdlist range, or if the FD was already stolen,
174
 * -1 is returned.
175
 *
176
 * Return: The FD at position @index is returned.
177
 */
178
0
int fdlist_steal(FDList *list, size_t index) {
179
0
        int *p, fd = -1;
180
181
0
        p = fdlist_data(list);
182
183
0
        if (index < fdlist_count(list)) {
184
0
                fd = p[index];
185
0
                p[index] = -1;
186
0
        }
187
188
0
        return fd;
189
0
}