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