File Coverage

marshal.c
Criterion Covered Total %
statement 57 136 41.9
branch 17 76 22.3
condition n/a
subroutine n/a
pod n/a
total 74 212 34.9


line stmt bran cond sub pod time code
1             /*
2             * marshal.c - implementation of the parallel-job binary format.
3             *
4             * Pure C, no Perl dependency. Big-endian on the wire so behaviour is
5             * stable across architectures (we already pay one byte-swap per int
6             * on x86; the cost is negligible against the syscalls).
7             */
8              
9             #include "marshal.h"
10              
11             #include
12             #include
13             #include
14             #include
15              
16             /* ============================================================
17             * Endian helpers
18             * ============================================================ */
19              
20             static void
21 697           put_u32(char *p, uint32_t v)
22             {
23 697           p[0] = (char)((v >> 24) & 0xff);
24 697           p[1] = (char)((v >> 16) & 0xff);
25 697           p[2] = (char)((v >> 8) & 0xff);
26 697           p[3] = (char)( v & 0xff);
27 697           }
28              
29             static uint32_t
30 0           get_u32(const char *p)
31             {
32 0           return ((uint32_t)(unsigned char)p[0] << 24)
33 0           | ((uint32_t)(unsigned char)p[1] << 16)
34 0           | ((uint32_t)(unsigned char)p[2] << 8)
35 0           | ((uint32_t)(unsigned char)p[3]);
36             }
37              
38             static void
39 63           put_u64(char *p, uint64_t v)
40             {
41 63           put_u32(p, (uint32_t)(v >> 32));
42 63           put_u32(p + 4, (uint32_t)(v & 0xffffffffu));
43 63           }
44              
45             static uint64_t
46 0           get_u64(const char *p)
47             {
48 0           return ((uint64_t)get_u32(p) << 32)
49 0           | ((uint64_t)get_u32(p + 4));
50             }
51              
52             /* ============================================================
53             * Marshal (build payload)
54             * ============================================================ */
55              
56             int
57 63           marshal_job(const char *path, size_t path_len,
58             const char *content, size_t content_len,
59             uint32_t mode,
60             uint64_t mtime, uint32_t mtime_ns,
61             uint32_t uid, uint32_t gid,
62             int apply_xattrs,
63             const ArchiveXattr *xattrs, size_t xattr_count,
64             char **out_payload, size_t *out_len)
65             {
66 63           size_t total =
67             4 + path_len + 1 + /* path_len + path + NUL */
68 63           4 + content_len + /* content_len + content */
69             4 + /* mode */
70             8 + 4 + /* mtime + mtime_ns */
71             4 + 4 + /* uid + gid */
72             4 + /* apply_xattrs */
73             4; /* xattr_count */
74              
75             size_t i;
76 65 100         for (i = 0; i < xattr_count; i++) {
77 2           total += 4 + xattrs[i].key_len + 1 /* key_len + key + NUL */
78 2           + 4 + xattrs[i].value_len; /* value_len + value */
79             }
80              
81 63           char *buf = (char *)malloc(total);
82 63 50         if (!buf) return -1;
83 63           char *p = buf;
84              
85 63           put_u32(p, (uint32_t)path_len); p += 4;
86 63 50         if (path_len) memcpy(p, path, path_len);
87 63           p += path_len;
88 63           *p++ = '\0';
89              
90 63           put_u32(p, (uint32_t)content_len); p += 4;
91 63 50         if (content_len) memcpy(p, content, content_len);
92 63           p += content_len;
93              
94 63           put_u32(p, mode); p += 4;
95 63           put_u64(p, mtime); p += 8;
96 63           put_u32(p, mtime_ns); p += 4;
97 63           put_u32(p, uid); p += 4;
98 63           put_u32(p, gid); p += 4;
99 63 100         put_u32(p, (uint32_t)(apply_xattrs ? 1 : 0)); p += 4;
100 63           put_u32(p, (uint32_t)xattr_count); p += 4;
101              
102 65 100         for (i = 0; i < xattr_count; i++) {
103 2           put_u32(p, (uint32_t)xattrs[i].key_len); p += 4;
104 2 50         if (xattrs[i].key_len) memcpy(p, xattrs[i].key, xattrs[i].key_len);
105 2           p += xattrs[i].key_len;
106 2           *p++ = '\0';
107 2           put_u32(p, (uint32_t)xattrs[i].value_len); p += 4;
108 2 50         if (xattrs[i].value_len) memcpy(p, xattrs[i].value, xattrs[i].value_len);
109 2           p += xattrs[i].value_len;
110             }
111              
112 63           *out_payload = buf;
113 63           *out_len = total;
114 63           return 0;
115             }
116              
117             /* ============================================================
118             * IO (length-prefixed wire transfer)
119             * ============================================================ */
120              
121             static int
122 126           write_full(int fd, const char *buf, size_t len)
123             {
124 126           size_t put = 0;
125 252 100         while (put < len) {
126 126           ssize_t w = write(fd, buf + put, len - put);
127 126 50         if (w < 0) {
128 0 0         if (errno == EINTR) continue;
129 0           return -1;
130             }
131 126 50         if (w == 0) { errno = EIO; return -1; }
132 126           put += (size_t)w;
133             }
134 126           return 0;
135             }
136              
137             static int
138 0           read_full(int fd, char *buf, size_t len)
139             {
140 0           size_t got = 0;
141 0 0         while (got < len) {
142 0           ssize_t n = read(fd, buf + got, len - got);
143 0 0         if (n < 0) {
144 0 0         if (errno == EINTR) continue;
145 0           return -1;
146             }
147 0 0         if (n == 0) return (int)got; /* EOF */
148 0           got += (size_t)n;
149             }
150 0           return (int)got;
151             }
152              
153             int
154 63           marshal_send(int fd, const char *payload, size_t payload_len)
155             {
156             char hdr[4];
157 63           put_u32(hdr, (uint32_t)payload_len);
158 63 50         if (write_full(fd, hdr, 4) < 0) return -1;
159 63 50         if (write_full(fd, payload, payload_len) < 0) return -1;
160 63           return 0;
161             }
162              
163             /* ============================================================
164             * Unmarshal (read job from fd)
165             * ============================================================ */
166              
167             #define MARSHAL_MAX_RECORD (256u * 1024u * 1024u) /* 256 MiB sanity cap */
168              
169             int
170 0           marshal_read_job(int fd, ParallelJob *out)
171             {
172             char hdr[4];
173 0           int n = read_full(fd, hdr, 4);
174 0 0         if (n == 0) return 1; /* clean EOF */
175 0 0         if (n < 4) return -1; /* truncated header */
176              
177 0           uint32_t total = get_u32(hdr);
178 0 0         if (total == 0 || total > MARSHAL_MAX_RECORD) return -1;
    0          
179              
180 0           char *arena = (char *)malloc(total);
181 0 0         if (!arena) return -1;
182              
183 0           n = read_full(fd, arena, total);
184 0 0         if (n != (int)total) { free(arena); return -1; }
185              
186 0           memset(out, 0, sizeof *out);
187 0           out->_arena = arena;
188 0           out->_arena_len = total;
189              
190 0           char *p = arena;
191 0           char *end = arena + total;
192              
193             /* path */
194 0 0         if (p + 4 > end) goto bad;
195 0           out->path_len = get_u32(p); p += 4;
196 0 0         if (p + out->path_len + 1 > end) goto bad;
197 0           out->path = p;
198 0           p += out->path_len + 1; /* skip the NUL */
199              
200             /* content */
201 0 0         if (p + 4 > end) goto bad;
202 0           out->content_len = get_u32(p); p += 4;
203 0 0         if (p + out->content_len > end) goto bad;
204 0           out->content = p;
205 0           p += out->content_len;
206              
207             /* fixed-shape metadata block */
208 0 0         if (p + 4 + 8 + 4 + 4 + 4 + 4 + 4 > end) goto bad;
209 0           out->mode = get_u32(p); p += 4;
210 0           out->mtime = get_u64(p); p += 8;
211 0           out->mtime_ns = get_u32(p); p += 4;
212 0           out->uid = get_u32(p); p += 4;
213 0           out->gid = get_u32(p); p += 4;
214 0           out->apply_xattrs = (int)get_u32(p); p += 4;
215 0           out->xattr_count = get_u32(p); p += 4;
216              
217 0 0         if (out->xattr_count > 0) {
218 0 0         if (out->xattr_count > MARSHAL_MAX_RECORD / sizeof(ArchiveXattr)) goto bad;
219 0           out->xattrs = (ArchiveXattr *)calloc(out->xattr_count, sizeof(ArchiveXattr));
220 0 0         if (!out->xattrs) goto bad;
221             size_t i;
222 0 0         for (i = 0; i < out->xattr_count; i++) {
223 0 0         if (p + 4 > end) goto bad;
224 0           out->xattrs[i].key_len = get_u32(p); p += 4;
225 0 0         if (p + out->xattrs[i].key_len + 1 > end) goto bad;
226 0           out->xattrs[i].key = p;
227 0           p += out->xattrs[i].key_len + 1;
228 0 0         if (p + 4 > end) goto bad;
229 0           out->xattrs[i].value_len = get_u32(p); p += 4;
230 0 0         if (p + out->xattrs[i].value_len > end) goto bad;
231 0           out->xattrs[i].value = p;
232 0           p += out->xattrs[i].value_len;
233             }
234             }
235              
236 0           return 0;
237              
238 0           bad:
239 0           free(out->xattrs);
240 0           free(arena);
241 0           memset(out, 0, sizeof *out);
242 0           return -1;
243             }
244              
245             void
246 0           parallel_job_free(ParallelJob *j)
247             {
248 0 0         if (!j) return;
249 0           free(j->_arena);
250 0           free(j->xattrs);
251 0           memset(j, 0, sizeof *j);
252             }