xref: /openbmc/u-boot/env/flags.c (revision 407b5b956a2e0facf6668fc8b295f4be9205c83e)
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
567