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