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