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