79dccc05fe27e60964f6e8948c4c6827e886ee2d
[oweals/u-boot.git] / env / flags.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2012
4  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
5  */
6
7 #include <linux/string.h>
8 #include <linux/ctype.h>
9
10 #ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
11 #include <stdint.h>
12 #include <stdio.h>
13 #include "fw_env_private.h"
14 #include "fw_env.h"
15 #include <env_attr.h>
16 #include <env_flags.h>
17 #define env_get fw_getenv
18 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
19 #else
20 #include <common.h>
21 #include <environment.h>
22 #endif
23
24 #ifdef CONFIG_CMD_NET
25 #define ENV_FLAGS_NET_VARTYPE_REPS "im"
26 #else
27 #define ENV_FLAGS_NET_VARTYPE_REPS ""
28 #endif
29
30 static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
31 static const char env_flags_varaccess_rep[] = "aroc";
32 static const int env_flags_varaccess_mask[] = {
33         0,
34         ENV_FLAGS_VARACCESS_PREVENT_DELETE |
35                 ENV_FLAGS_VARACCESS_PREVENT_CREATE |
36                 ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
37         ENV_FLAGS_VARACCESS_PREVENT_DELETE |
38                 ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
39         ENV_FLAGS_VARACCESS_PREVENT_DELETE |
40                 ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR};
41
42 #ifdef CONFIG_CMD_ENV_FLAGS
43 static const char * const env_flags_vartype_names[] = {
44         "string",
45         "decimal",
46         "hexadecimal",
47         "boolean",
48 #ifdef CONFIG_CMD_NET
49         "IP address",
50         "MAC address",
51 #endif
52 };
53 static const char * const env_flags_varaccess_names[] = {
54         "any",
55         "read-only",
56         "write-once",
57         "change-default",
58 };
59
60 /*
61  * Print the whole list of available type flags.
62  */
63 void env_flags_print_vartypes(void)
64 {
65         enum env_flags_vartype curtype = (enum env_flags_vartype)0;
66
67         while (curtype != env_flags_vartype_end) {
68                 printf("\t%c   -\t%s\n", env_flags_vartype_rep[curtype],
69                         env_flags_vartype_names[curtype]);
70                 curtype++;
71         }
72 }
73
74 /*
75  * Print the whole list of available access flags.
76  */
77 void env_flags_print_varaccess(void)
78 {
79         enum env_flags_varaccess curaccess = (enum env_flags_varaccess)0;
80
81         while (curaccess != env_flags_varaccess_end) {
82                 printf("\t%c   -\t%s\n", env_flags_varaccess_rep[curaccess],
83                         env_flags_varaccess_names[curaccess]);
84                 curaccess++;
85         }
86 }
87
88 /*
89  * Return the name of the type.
90  */
91 const char *env_flags_get_vartype_name(enum env_flags_vartype type)
92 {
93         return env_flags_vartype_names[type];
94 }
95
96 /*
97  * Return the name of the access.
98  */
99 const char *env_flags_get_varaccess_name(enum env_flags_varaccess access)
100 {
101         return env_flags_varaccess_names[access];
102 }
103 #endif /* CONFIG_CMD_ENV_FLAGS */
104
105 /*
106  * Parse the flags string from a .flags attribute list into the vartype enum.
107  */
108 enum env_flags_vartype env_flags_parse_vartype(const char *flags)
109 {
110         char *type;
111
112         if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
113                 return env_flags_vartype_string;
114
115         type = strchr(env_flags_vartype_rep,
116                 flags[ENV_FLAGS_VARTYPE_LOC]);
117
118         if (type != NULL)
119                 return (enum env_flags_vartype)
120                         (type - &env_flags_vartype_rep[0]);
121
122         printf("## Warning: Unknown environment variable type '%c'\n",
123                 flags[ENV_FLAGS_VARTYPE_LOC]);
124         return env_flags_vartype_string;
125 }
126
127 /*
128  * Parse the flags string from a .flags attribute list into the varaccess enum.
129  */
130 enum env_flags_varaccess env_flags_parse_varaccess(const char *flags)
131 {
132         char *access;
133
134         if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
135                 return env_flags_varaccess_any;
136
137         access = strchr(env_flags_varaccess_rep,
138                 flags[ENV_FLAGS_VARACCESS_LOC]);
139
140         if (access != NULL)
141                 return (enum env_flags_varaccess)
142                         (access - &env_flags_varaccess_rep[0]);
143
144         printf("## Warning: Unknown environment variable access method '%c'\n",
145                 flags[ENV_FLAGS_VARACCESS_LOC]);
146         return env_flags_varaccess_any;
147 }
148
149 /*
150  * Parse the binary flags from a hash table entry into the varaccess enum.
151  */
152 enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags)
153 {
154         int i;
155
156         for (i = 0; i < ARRAY_SIZE(env_flags_varaccess_mask); i++)
157                 if (env_flags_varaccess_mask[i] ==
158                     (binflags & ENV_FLAGS_VARACCESS_BIN_MASK))
159                         return (enum env_flags_varaccess)i;
160
161         printf("Warning: Non-standard access flags. (0x%x)\n",
162                 binflags & ENV_FLAGS_VARACCESS_BIN_MASK);
163
164         return env_flags_varaccess_any;
165 }
166
167 static inline int is_hex_prefix(const char *value)
168 {
169         return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
170 }
171
172 static void skip_num(int hex, const char *value, const char **end,
173         int max_digits)
174 {
175         int i;
176
177         if (hex && is_hex_prefix(value))
178                 value += 2;
179
180         for (i = max_digits; i != 0; i--) {
181                 if (hex && !isxdigit(*value))
182                         break;
183                 if (!hex && !isdigit(*value))
184                         break;
185                 value++;
186         }
187         if (end != NULL)
188                 *end = value;
189 }
190
191 #ifdef CONFIG_CMD_NET
192 int eth_validate_ethaddr_str(const char *addr)
193 {
194         const char *end;
195         const char *cur;
196         int i;
197
198         cur = addr;
199         for (i = 0; i < 6; i++) {
200                 skip_num(1, cur, &end, 2);
201                 if (cur == end)
202                         return -1;
203                 if (cur + 2 == end && is_hex_prefix(cur))
204                         return -1;
205                 if (i != 5 && *end != ':')
206                         return -1;
207                 if (i == 5 && *end != '\0')
208                         return -1;
209                 cur = end + 1;
210         }
211
212         return 0;
213 }
214 #endif
215
216 /*
217  * Based on the declared type enum, validate that the value string complies
218  * with that format
219  */
220 static int _env_flags_validate_type(const char *value,
221         enum env_flags_vartype type)
222 {
223         const char *end;
224 #ifdef CONFIG_CMD_NET
225         const char *cur;
226         int i;
227 #endif
228
229         switch (type) {
230         case env_flags_vartype_string:
231                 break;
232         case env_flags_vartype_decimal:
233                 skip_num(0, value, &end, -1);
234                 if (*end != '\0')
235                         return -1;
236                 break;
237         case env_flags_vartype_hex:
238                 skip_num(1, value, &end, -1);
239                 if (*end != '\0')
240                         return -1;
241                 if (value + 2 == end && is_hex_prefix(value))
242                         return -1;
243                 break;
244         case env_flags_vartype_bool:
245                 if (value[0] != '1' && value[0] != 'y' && value[0] != 't' &&
246                     value[0] != 'Y' && value[0] != 'T' &&
247                     value[0] != '0' && value[0] != 'n' && value[0] != 'f' &&
248                     value[0] != 'N' && value[0] != 'F')
249                         return -1;
250                 if (value[1] != '\0')
251                         return -1;
252                 break;
253 #ifdef CONFIG_CMD_NET
254         case env_flags_vartype_ipaddr:
255                 cur = value;
256                 for (i = 0; i < 4; i++) {
257                         skip_num(0, cur, &end, 3);
258                         if (cur == end)
259                                 return -1;
260                         if (i != 3 && *end != '.')
261                                 return -1;
262                         if (i == 3 && *end != '\0')
263                                 return -1;
264                         cur = end + 1;
265                 }
266                 break;
267         case env_flags_vartype_macaddr:
268                 if (eth_validate_ethaddr_str(value))
269                         return -1;
270                 break;
271 #endif
272         case env_flags_vartype_end:
273                 return -1;
274         }
275
276         /* OK */
277         return 0;
278 }
279
280 /*
281  * Look for flags in a provided list and failing that the static list
282  */
283 static inline int env_flags_lookup(const char *flags_list, const char *name,
284         char *flags)
285 {
286         int ret = 1;
287
288         if (!flags)
289                 /* bad parameter */
290                 return -1;
291
292         /* try the env first */
293         if (flags_list)
294                 ret = env_attr_lookup(flags_list, name, flags);
295
296         if (ret != 0)
297                 /* if not found in the env, look in the static list */
298                 ret = env_attr_lookup(ENV_FLAGS_LIST_STATIC, name, flags);
299
300         return ret;
301 }
302
303 #ifdef USE_HOSTCC /* Functions only used from tools/env */
304 /*
305  * Look up any flags directly from the .flags variable and the static list
306  * and convert them to the vartype enum.
307  */
308 enum env_flags_vartype env_flags_get_type(const char *name)
309 {
310         const char *flags_list = env_get(ENV_FLAGS_VAR);
311         char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
312
313         if (env_flags_lookup(flags_list, name, flags))
314                 return env_flags_vartype_string;
315
316         if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
317                 return env_flags_vartype_string;
318
319         return env_flags_parse_vartype(flags);
320 }
321
322 /*
323  * Look up the access of a variable directly from the .flags var.
324  */
325 enum env_flags_varaccess env_flags_get_varaccess(const char *name)
326 {
327         const char *flags_list = env_get(ENV_FLAGS_VAR);
328         char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
329
330         if (env_flags_lookup(flags_list, name, flags))
331                 return env_flags_varaccess_any;
332
333         if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
334                 return env_flags_varaccess_any;
335
336         return env_flags_parse_varaccess(flags);
337 }
338
339 /*
340  * Validate that the proposed new value for "name" is valid according to the
341  * defined flags for that variable, if any.
342  */
343 int env_flags_validate_type(const char *name, const char *value)
344 {
345         enum env_flags_vartype type;
346
347         if (value == NULL)
348                 return 0;
349         type = env_flags_get_type(name);
350         if (_env_flags_validate_type(value, type) < 0) {
351                 printf("## Error: flags type check failure for "
352                         "\"%s\" <= \"%s\" (type: %c)\n",
353                         name, value, env_flags_vartype_rep[type]);
354                 return -1;
355         }
356         return 0;
357 }
358
359 /*
360  * Validate that the proposed access to variable "name" is valid according to
361  * the defined flags for that variable, if any.
362  */
363 int env_flags_validate_varaccess(const char *name, int check_mask)
364 {
365         enum env_flags_varaccess access;
366         int access_mask;
367
368         access = env_flags_get_varaccess(name);
369         access_mask = env_flags_varaccess_mask[access];
370
371         return (check_mask & access_mask) != 0;
372 }
373
374 /*
375  * Validate the parameters to "env set" directly
376  */
377 int env_flags_validate_env_set_params(char *name, char * const val[], int count)
378 {
379         if ((count >= 1) && val[0] != NULL) {
380                 enum env_flags_vartype type = env_flags_get_type(name);
381
382                 /*
383                  * we don't currently check types that need more than
384                  * one argument
385                  */
386                 if (type != env_flags_vartype_string && count > 1) {
387                         printf("## Error: too many parameters for setting \"%s\"\n",
388                                name);
389                         return -1;
390                 }
391                 return env_flags_validate_type(name, val[0]);
392         }
393         /* ok */
394         return 0;
395 }
396
397 #else /* !USE_HOSTCC - Functions only used from lib/hashtable.c */
398
399 /*
400  * Parse the flag charachters from the .flags attribute list into the binary
401  * form to be stored in the environment entry->flags field.
402  */
403 static int env_parse_flags_to_bin(const char *flags)
404 {
405         int binflags;
406
407         binflags = env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
408         binflags |= env_flags_varaccess_mask[env_flags_parse_varaccess(flags)];
409
410         return binflags;
411 }
412
413 static int first_call = 1;
414 static const char *flags_list;
415
416 /*
417  * Look for possible flags for a newly added variable
418  * This is called specifically when the variable did not exist in the hash
419  * previously, so the blanket update did not find this variable.
420  */
421 void env_flags_init(ENTRY *var_entry)
422 {
423         const char *var_name = var_entry->key;
424         char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = "";
425         int ret = 1;
426
427         if (first_call) {
428                 flags_list = env_get(ENV_FLAGS_VAR);
429                 first_call = 0;
430         }
431         /* look in the ".flags" and static for a reference to this variable */
432         ret = env_flags_lookup(flags_list, var_name, flags);
433
434         /* if any flags were found, set the binary form to the entry */
435         if (!ret && strlen(flags))
436                 var_entry->flags = env_parse_flags_to_bin(flags);
437 }
438
439 /*
440  * Called on each existing env var prior to the blanket update since removing
441  * a flag in the flag list should remove its flags.
442  */
443 static int clear_flags(ENTRY *entry)
444 {
445         entry->flags = 0;
446
447         return 0;
448 }
449
450 /*
451  * Call for each element in the list that defines flags for a variable
452  */
453 static int set_flags(const char *name, const char *value, void *priv)
454 {
455         ENTRY e, *ep;
456
457         e.key   = name;
458         e.data  = NULL;
459         e.callback = NULL;
460         hsearch_r(e, FIND, &ep, &env_htab, 0);
461
462         /* does the env variable actually exist? */
463         if (ep != NULL) {
464                 /* the flag list is empty, so clear the flags */
465                 if (value == NULL || strlen(value) == 0)
466                         ep->flags = 0;
467                 else
468                         /* assign the requested flags */
469                         ep->flags = env_parse_flags_to_bin(value);
470         }
471
472         return 0;
473 }
474
475 static int on_flags(const char *name, const char *value, enum env_op op,
476         int flags)
477 {
478         /* remove all flags */
479         hwalk_r(&env_htab, clear_flags);
480
481         /* configure any static flags */
482         env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags, NULL);
483         /* configure any dynamic flags */
484         env_attr_walk(value, set_flags, NULL);
485
486         return 0;
487 }
488 U_BOOT_ENV_CALLBACK(flags, on_flags);
489
490 /*
491  * Perform consistency checking before creating, overwriting, or deleting an
492  * environment variable. Called as a callback function by hsearch_r() and
493  * hdelete_r(). Returns 0 in case of success, 1 in case of failure.
494  * When (flag & H_FORCE) is set, do not print out any error message and force
495  * overwriting of write-once variables.
496  */
497
498 int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
499         int flag)
500 {
501         const char *name;
502         const char *oldval = NULL;
503
504         if (op != env_op_create)
505                 oldval = item->data;
506
507         name = item->key;
508
509         /* Default value for NULL to protect string-manipulating functions */
510         newval = newval ? : "";
511
512         /* validate the value to match the variable type */
513         if (op != env_op_delete) {
514                 enum env_flags_vartype type = (enum env_flags_vartype)
515                         (ENV_FLAGS_VARTYPE_BIN_MASK & item->flags);
516
517                 if (_env_flags_validate_type(newval, type) < 0) {
518                         printf("## Error: flags type check failure for "
519                                 "\"%s\" <= \"%s\" (type: %c)\n",
520                                 name, newval, env_flags_vartype_rep[type]);
521                         return -1;
522                 }
523         }
524
525         /* check for access permission */
526 #ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE
527         if (flag & H_FORCE)
528                 return 0;
529 #endif
530         switch (op) {
531         case env_op_delete:
532                 if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_DELETE) {
533                         printf("## Error: Can't delete \"%s\"\n", name);
534                         return 1;
535                 }
536                 break;
537         case env_op_overwrite:
538                 if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_OVERWR) {
539                         printf("## Error: Can't overwrite \"%s\"\n", name);
540                         return 1;
541                 } else if (item->flags &
542                     ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR) {
543                         const char *defval = env_get_default(name);
544
545                         if (defval == NULL)
546                                 defval = "";
547                         printf("oldval: %s  defval: %s\n", oldval, defval);
548                         if (strcmp(oldval, defval) != 0) {
549                                 printf("## Error: Can't overwrite \"%s\"\n",
550                                         name);
551                                 return 1;
552                         }
553                 }
554                 break;
555         case env_op_create:
556                 if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_CREATE) {
557                         printf("## Error: Can't create \"%s\"\n", name);
558                         return 1;
559                 }
560                 break;
561         }
562
563         return 0;
564 }
565
566 #endif