Increase protocol payload size
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <asm/byteorder.h>
7
8 #include <talloc/talloc.h>
9 #include <list/list.h>
10 #include <log/log.h>
11
12 #include "pb-protocol.h"
13
14
15 /* Message format:
16  *
17  * 4-byte action, determines the remaining message content
18  * 4-byte total payload len
19  *   - not including action and payload len header
20  *
21  * action = 0x1: device add message
22  *  payload:
23  *   4-byte len, id
24  *   4-byte len, name
25  *   4-byte len, description
26  *   4-byte len, icon_file
27  *
28  *   4-byte option count
29  *   for each option:
30  *    4-byte len, id
31  *    4-byte len, name
32  *    4-byte len, description
33  *    4-byte len, icon_file
34  *    4-byte len, boot_image_file
35  *    4-byte len, initrd_file
36  *    4-byte len, boot_args
37  *
38  * action = 0x2: device remove message
39  *  payload:
40  *   4-byte len, id
41  */
42
43 int pb_protocol_device_cmp(const struct device *a, const struct device *b)
44 {
45         return !strcmp(a->id, b->id);
46 }
47
48 int pb_protocol_boot_option_cmp(const struct boot_option *a,
49         const struct boot_option *b)
50 {
51         return !strcmp(a->id, b->id);
52 }
53
54 /* Write a string into the buffer, starting at pos.
55  *
56  * Returns the total length used for the write, including length header.
57  */
58 int pb_protocol_serialise_string(char *pos, const char *str)
59 {
60         int len = 0;
61
62         if (str)
63                 len = strlen(str);
64
65         *(uint32_t *)pos = __cpu_to_be32(len);
66         pos += sizeof(uint32_t);
67
68         memcpy(pos, str, len);
69
70         return len + sizeof(uint32_t);
71 }
72
73 /* Read a string from a buffer, allocating the new string as necessary.
74  *
75  * @param[in] ctx       The talloc context to base the allocation on
76  * @param[in,out] pos   Where to start reading
77  * @param[in,out] len   The amount of data remaining in the buffer
78  * @param[out] str      Pointer to resuling string
79  * @return              zero on success, non-zero on failure
80  */
81 static int read_string(void *ctx, const char **pos, unsigned int *len,
82         char **str)
83 {
84         uint32_t str_len, read_len;
85
86         if (*len < sizeof(uint32_t))
87                 return -1;
88
89         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
90         read_len = sizeof(uint32_t);
91
92         if (read_len + str_len > *len)
93                 return -1;
94
95         if (str_len == 0)
96                 *str = NULL;
97         else
98                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
99
100         read_len += str_len;
101
102         /* all ok, update the caller's pointers */
103         *pos += read_len;
104         *len -= read_len;
105
106         return 0;
107 }
108
109 char *pb_protocol_deserialise_string(void *ctx,
110                 const struct pb_protocol_message *message)
111 {
112         const char *buf;
113         char *str;
114         unsigned int len;
115
116         len = message->payload_len;
117         buf = message->payload;
118
119         if (read_string(ctx, &buf, &len, &str))
120                 return NULL;
121
122         return str;
123 }
124
125 static int optional_strlen(const char *str)
126 {
127         if (!str)
128                 return 0;
129         return strlen(str);
130 }
131
132 int pb_protocol_device_len(const struct device *dev)
133 {
134         struct boot_option *opt;
135         int len;
136
137         len =   4 + optional_strlen(dev->id) +
138                 4 + optional_strlen(dev->name) +
139                 4 + optional_strlen(dev->description) +
140                 4 + optional_strlen(dev->icon_file) +
141                 4;
142
143         list_for_each_entry(&dev->boot_options, opt, list) {
144                 len +=  4 + optional_strlen(opt->id) +
145                         4 + optional_strlen(opt->name) +
146                         4 + optional_strlen(opt->description) +
147                         4 + optional_strlen(opt->icon_file) +
148                         4 + optional_strlen(opt->boot_image_file) +
149                         4 + optional_strlen(opt->initrd_file) +
150                         4 + optional_strlen(opt->boot_args);
151         }
152
153         return len;
154 }
155
156 int pb_protocol_serialise_device(const struct device *dev, char *buf, int buf_len)
157 {
158         struct boot_option *opt;
159         uint32_t n;
160         char *pos;
161
162         pos = buf;
163
164         /* construct payload into buffer */
165         pos += pb_protocol_serialise_string(pos, dev->id);
166         pos += pb_protocol_serialise_string(pos, dev->name);
167         pos += pb_protocol_serialise_string(pos, dev->description);
168         pos += pb_protocol_serialise_string(pos, dev->icon_file);
169
170         /* write option count */
171         n = 0;
172
173         list_for_each_entry(&dev->boot_options, opt, list)
174                 n++;
175
176         *(uint32_t *)pos = __cpu_to_be32(n);
177         pos += sizeof(uint32_t);
178
179         /* write each option */
180         list_for_each_entry(&dev->boot_options, opt, list) {
181                 pos += pb_protocol_serialise_string(pos, opt->id);
182                 pos += pb_protocol_serialise_string(pos, opt->name);
183                 pos += pb_protocol_serialise_string(pos, opt->description);
184                 pos += pb_protocol_serialise_string(pos, opt->icon_file);
185                 pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
186                 pos += pb_protocol_serialise_string(pos, opt->initrd_file);
187                 pos += pb_protocol_serialise_string(pos, opt->boot_args);
188         }
189
190         assert(pos <= buf + buf_len);
191
192         return 0;
193 }
194
195 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
196 {
197         int total_len, rc;
198         char *pos;
199
200         total_len = sizeof(*message) + message->payload_len;
201
202         message->payload_len = __cpu_to_be32(message->payload_len);
203         message->action = __cpu_to_be32(message->action);
204
205         for (pos = (void *)message; total_len;) {
206                 rc = write(fd, pos, total_len);
207
208                 if (rc <= 0)
209                         break;
210
211                 total_len -= rc;
212                 pos += rc;
213         }
214
215         talloc_free(message);
216
217         if (!total_len)
218                 return 0;
219
220         pb_log("%s: failed: %s\n", __func__, strerror(errno));
221         return -1;
222 }
223
224 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
225                 enum pb_protocol_action action, int payload_len)
226 {
227         struct pb_protocol_message *message;
228
229         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
230                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
231                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
232                 return NULL;
233         }
234
235         message = talloc_size(ctx, sizeof(*message) + payload_len);
236
237         /* we convert these to big-endian in write_message() */
238         message->action = action;
239         message->payload_len = payload_len;
240
241         return message;
242
243 }
244
245 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
246 {
247         struct pb_protocol_message *message, m;
248         int rc;
249         unsigned int len;
250
251         /* use the stack for the initial 8-byte read */
252
253         rc = read(fd, &m, sizeof(m));
254         if (rc != sizeof(m))
255                 return NULL;
256
257         m.payload_len = __be32_to_cpu(m.payload_len);
258         m.action = __be32_to_cpu(m.action);
259
260         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
261                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
262                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
263                 return NULL;
264         }
265
266         message = talloc_size(ctx, sizeof(m) + m.payload_len);
267         memcpy(message, &m, sizeof(m));
268
269         for (len = 0; len < m.payload_len;) {
270                 rc = read(fd, message->payload + len, m.payload_len - len);
271
272                 if (rc <= 0) {
273                         talloc_free(message);
274                         pb_log("%s: failed (%u): %s\n", __func__, len,
275                                 strerror(errno));
276                         return NULL;
277                 }
278
279                 len += rc;
280         }
281
282         return message;
283 }
284
285
286 struct device *pb_protocol_deserialise_device(void *ctx,
287                 const struct pb_protocol_message *message)
288 {
289         struct device *dev;
290         const char *pos;
291         int i, n_options;
292         unsigned int len;
293
294         len = message->payload_len;
295         pos = message->payload;
296
297         dev = talloc(ctx, struct device);
298
299         if (read_string(dev, &pos, &len, &dev->id))
300                 goto out_err;
301
302         if (read_string(dev, &pos, &len, &dev->name))
303                 goto out_err;
304
305         if (read_string(dev, &pos, &len, &dev->description))
306                 goto out_err;
307
308         if (read_string(dev, &pos, &len, &dev->icon_file))
309                 goto out_err;
310
311         n_options = __be32_to_cpu(*(uint32_t *)pos);
312         pos += sizeof(uint32_t);
313
314         list_init(&dev->boot_options);
315
316         for (i = 0; i < n_options; i++) {
317                 struct boot_option *opt;
318
319                 opt = talloc(dev, struct boot_option);
320
321                 if (read_string(opt, &pos, &len, &opt->id))
322                         goto out_err;
323                 if (read_string(opt, &pos, &len, &opt->name))
324                         goto out_err;
325                 if (read_string(opt, &pos, &len,
326                                         &opt->description))
327                         goto out_err;
328                 if (read_string(opt, &pos, &len,
329                                         &opt->icon_file))
330                         goto out_err;
331                 if (read_string(opt, &pos, &len,
332                                         &opt->boot_image_file))
333                         goto out_err;
334                 if (read_string(opt, &pos, &len,
335                                         &opt->initrd_file))
336                         goto out_err;
337                 if (read_string(opt, &pos, &len,
338                                         &opt->boot_args))
339                         goto out_err;
340
341                 list_add(&dev->boot_options, &opt->list);
342         }
343
344         return dev;
345
346 out_err:
347         talloc_free(dev);
348         return NULL;
349 }