1 #define _GNU_SOURCE
2 #include <dlfcn.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdint.h>
10 #include <assert.h>
11 #include <stdbool.h>
12 #include <ffi.h>
13 #include <inttypes.h>
14
15 #include "builtins.h"
16 #include "variables.h"
17 #include "arrayfunc.h"
18 #include "common.h"
19 #include "bashgetopt.h"
20 #include "make_cmd.h"
21 #include "util.h"
22 #include "types.h"
23 #include "shell.h"
24
25 #if !defined(__GLIBC__) && !defined(__NEWLIB__) && !defined(__DragonFly__)
mempcpy(void * dest,const void * src,size_t n)26 static inline void *mempcpy(void *dest, const void *src, size_t n)
27 {
28 memcpy(dest, src, n);
29 return ((char *)dest + n);
30 }
31 #endif
32
33 struct pack_context {
34 ffi_type *ptrtype;
35 WORD_LIST *list;
36 uint8_t *source;
37 int retval;
38 };
39
40
41 // Bash provides an array_walk() and an assoc_walk() to invoke a callback on
42 // each element of an array and associative array respectively. Unfortunately
43 // assoc_walk does not accept a user data parameter like array_walk, so we
44 // provide our own version.
assoc_walk_data(HASH_TABLE * table,int (* func)(BUCKET_CONTENTS *,void *),void * data)45 void assoc_walk_data(HASH_TABLE *table,
46 int (*func)(BUCKET_CONTENTS *, void *),
47 void *data)
48 {
49 int i;
50 BUCKET_CONTENTS *item;
51
52 if (table == 0 || HASH_ENTRIES (table) == 0)
53 return;
54
55 for (i = 0; i < table->nbuckets; i++)
56 {
57 for (item = hash_items (i, table); item; item = item->next)
58 if ((*func) (item, data) < 0)
59 return;
60 }
61 }
62
63 // Callback for each array element.
pack_decode_element(ARRAY_ELEMENT * element,void * user)64 int pack_decode_element(ARRAY_ELEMENT *element, void *user)
65 {
66 struct pack_context *ctx;
67 void **value;
68
69 ctx = user;
70
71 if (decode_primitive_type(element->value,
72 (void **)&value,
73 &ctx->ptrtype) == false) {
74
75 // You can exit from an array_walk early by returning -1, so set
76 // failure and do that here.
77 ctx->retval = EXECUTION_FAILURE;
78
79 // Give a hint about what failed to parse.
80 builtin_warning("aborted pack at bad type prefix %s (%s[%lu])",
81 element->value,
82 ctx->list->word->word,
83 element->ind);
84
85 return -1;
86 }
87
88 // Extract the data into the destination buffer.
89 ctx->source = mempcpy(ctx->source, value, ctx->ptrtype->size);
90
91 // No longer needed.
92 free(value);
93 return 0;
94 }
95
96 // Callback for each assoc element.
pack_decode_element_assoc(BUCKET_CONTENTS * element,void * user)97 int pack_decode_element_assoc(BUCKET_CONTENTS *element, void *user)
98 {
99 struct pack_context *ctx;
100 void **value;
101
102 ctx = user;
103
104 // Check if we've been passed an uninitialized type (therefore 0)
105 if (strchr(element->data, ':') == NULL) {
106 if (decode_type_prefix(element->data, "0", &ctx->ptrtype, (void **)&value, NULL) == true) {
107 // Looks like that worked, skip decoding.
108 goto decode;
109 }
110 }
111
112 if (decode_primitive_type(element->data,
113 (void **)&value,
114 &ctx->ptrtype) == false) {
115
116 // You can exit from an array_walk early by returning -1, so set
117 // failure and do that here.
118 ctx->retval = EXECUTION_FAILURE;
119
120 // Give a hint about what failed to parse.
121 builtin_warning("aborted pack at bad type prefix %s (%s[%s])",
122 (char *) element->data,
123 ctx->list->word->word,
124 element->key);
125
126 return -1;
127 }
128
129 decode:
130 // Extract the data into the destination buffer.
131 ctx->source = mempcpy(ctx->source, value, ctx->ptrtype->size);
132
133 // No longer needed.
134 free(value);
135
136 return 0;
137 }
138
pack_prefixed_array(WORD_LIST * list)139 static int pack_prefixed_array(WORD_LIST *list)
140 {
141 SHELL_VAR *dest_v;
142 ARRAY *dest_a;
143 HASH_TABLE *dest_h;
144 void **value;
145 struct pack_context ctx = { 0 };
146
147 // Assume success by default.
148 ctx.retval = EXECUTION_SUCCESS;
149
150 // Verify we have two parameters.
151 if (!list || !list->next) {
152 builtin_usage();
153 goto error;
154 }
155
156 // Check if the user forgot the '$'
157 if (strchr(list->word->word, ':') == NULL) {
158 builtin_warning("%s does not have a prefix, is that what you intended?", list->word->word);
159 }
160
161 // Fetch the source pointer.
162 if (decode_primitive_type(list->word->word,
163 (void **)&value,
164 &ctx.ptrtype) != true) {
165 builtin_error("the destination parameter %s could not parsed", list->word->word);
166 goto error;
167 }
168
169 // Verify that it was a pointer.
170 if (ctx.ptrtype != &ffi_type_pointer) {
171 builtin_error("the destination parameter must be a pointer");
172 goto error;
173 }
174
175 // Skip to next parameter.
176 list = list->next;
177 ctx.source = *value;
178 ctx.list = list;
179
180 GET_ARRAY_FROM_VAR(list->word->word, dest_v, dest_a);
181
182 if (assoc_p(dest_v)) {
183 // Extract the hash table
184 dest_h = (HASH_TABLE *) dest_v->value;
185
186 if (dest_h->nbuckets != 1) {
187 builtin_warning("the associative array %s will not maintain it's order", list->word->word);
188 }
189
190 assoc_walk_data(dest_h, pack_decode_element_assoc, &ctx);
191 } else if (array_p(dest_v)) {
192 array_walk(dest_a, pack_decode_element, &ctx);
193 } else {
194 builtin_error("expected an array or associative array");
195 goto error;
196 }
197
198 return ctx.retval;
199
200 error:
201 return EXECUTION_FAILURE;
202 }
203
204 struct unpack_context {
205 ffi_type *ptrtype;
206 WORD_LIST *list;
207 uint8_t *source;
208 int retval;
209 };
210
211 // Callback for each array element.
unpack_decode_element(ARRAY_ELEMENT * element,void * user)212 int unpack_decode_element(ARRAY_ELEMENT *element, void *user)
213 {
214 struct unpack_context *ctx;
215 char *format;
216
217 ctx = user;
218
219 // Truncate it if there's already a value, e.g.
220 // a=(int:0 int:0) is accceptable to initialize a buffer.
221 if ((format = strchr(element->value, ':')))
222 *format = '\0';
223
224 if (decode_type_prefix(element->value,
225 NULL,
226 &ctx->ptrtype,
227 NULL,
228 &format) == false) {
229 // You can exit from an array_walk early by returning -1, so set
230 // failure and do that here.
231 ctx->retval = EXECUTION_FAILURE;
232
233 // Give a hint about what failed to parse.
234 builtin_warning("aborted unpack at bad type prefix %s (%s[%lu])",
235 element->value,
236 ctx->list->word->word,
237 element->ind);
238
239 return -1;
240 }
241
242 // Discard previous value
243 FREE(element->value);
244
245 // Decode the type.
246 element->value = encode_primitive_type(format, ctx->ptrtype, ctx->source);
247
248 // Skip to next element.
249 ctx->source += ctx->ptrtype->size;
250
251 return 0;
252 }
253
254 // Callback for each assoc element.
unpack_decode_element_assoc(BUCKET_CONTENTS * element,void * user)255 int unpack_decode_element_assoc(BUCKET_CONTENTS *element, void *user)
256 {
257 struct unpack_context *ctx;
258 char *format;
259
260 ctx = user;
261
262 // Truncate it if there's already a value, e.g.
263 // a=(int:0 int:0) is accceptable to initialize a buffer.
264 if ((format = strchr(element->data, ':')))
265 *format = '\0';
266
267 if (decode_type_prefix(element->data,
268 NULL,
269 &ctx->ptrtype,
270 NULL,
271 &format) == false) {
272 // You can exit from an array_walk early by returning -1, so set
273 // failure and do that here.
274 ctx->retval = EXECUTION_FAILURE;
275
276 // Give a hint about what failed to parse.
277 builtin_warning("aborted unpack at bad type prefix %s (%s[%s])",
278 (char *) element->data,
279 ctx->list->word->word,
280 element->key);
281
282 return -1;
283 }
284
285 // Discard previous value
286 FREE(element->data);
287
288 // Decode the type.
289 element->data = encode_primitive_type(format, ctx->ptrtype, ctx->source);
290
291 // Skip to next element.
292 ctx->source += ctx->ptrtype->size;
293
294 return 0;
295 }
296
unpack_prefixed_array(WORD_LIST * list)297 static int unpack_prefixed_array(WORD_LIST *list)
298 {
299 SHELL_VAR *dest_v;
300 ARRAY *dest_a;
301 HASH_TABLE *dest_h;
302 void **value;
303 struct unpack_context ctx = { 0 };
304
305 // Assume success by default.
306 ctx.retval = EXECUTION_SUCCESS;
307
308 // Verify we have two parameters.
309 if (!list || !list->next) {
310 builtin_usage();
311 goto error;
312 }
313
314 // Check if the user forgot the '$'
315 if (strchr(list->word->word, ':') == NULL) {
316 builtin_warning("%s does not have a prefix, is that what you intended?", list->word->word);
317 }
318
319 // Fetch the source pointer.
320 if (decode_primitive_type(list->word->word,
321 (void **)&value,
322 &ctx.ptrtype) != true) {
323 builtin_error("the source parameter %s could not parsed", list->word->word);
324 goto error;
325 }
326
327 // Verify that it was a pointer.
328 if (ctx.ptrtype != &ffi_type_pointer) {
329 builtin_error("the source parameter must be a pointer");
330 goto error;
331 }
332
333 // Skip to next parameter.
334 list = list->next;
335 ctx.source = *value;
336 ctx.list = list;
337
338 GET_ARRAY_FROM_VAR(list->word->word, dest_v, dest_a);
339
340 if (assoc_p(dest_v)) {
341 // Extract the hash table
342 dest_h = (HASH_TABLE *) dest_v->value;
343
344 if (dest_h->nbuckets != 1) {
345 builtin_warning("the associative array %s will not maintain it's order", list->word->word);
346 }
347
348 assoc_walk_data(dest_h, unpack_decode_element_assoc, &ctx);
349 } else if (array_p(dest_v)) {
350 array_walk(dest_a, unpack_decode_element, &ctx);
351 } else {
352 builtin_error("expected an array or associative array");
353 goto error;
354 }
355
356 return ctx.retval;
357
358 error:
359 return EXECUTION_FAILURE;
360 }
361
362 static char *unpack_usage[] = {
363 "Unpack memory into a bash array.",
364 "",
365 "Bash provides no convenient mechanism for dealing with binary data, this",
366 "interface allows for converting between prefixed, native, primitive types.",
367 "",
368 "$ struct=(pointer char int long)",
369 "$ unpack pointer:0x1234 struct",
370 "$ echo ${struct[*]}",
371 "pointer:0x1234 char:a int:1234 long:-1",
372 " pack pointer:01234 struct",
373 "",
374 NULL,
375 };
376
377 static char *pack_usage[] = {
378 "Convert data from a prefixed bash array into native memory.",
379 NULL,
380 };
381
382 struct builtin __attribute__((visibility("default"))) unpack_struct = {
383 .name = "unpack",
384 .function = unpack_prefixed_array,
385 .flags = BUILTIN_ENABLED,
386 .long_doc = unpack_usage,
387 .short_doc = "unpack pointer array",
388 .handle = NULL,
389 };
390
391 struct builtin __attribute__((visibility("default"))) pack_struct = {
392 .name = "pack",
393 .function = pack_prefixed_array,
394 .flags = BUILTIN_ENABLED,
395 .long_doc = pack_usage,
396 .short_doc = "pack pointer array",
397 .handle = NULL,
398 };
399
400