]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/env_flags.c
a58d614bb3f63e871a63500057b484dbd9305f1d
[karo-tx-uboot.git] / common / env_flags.c
1 /*
2  * (C) Copyright 2012
3  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <linux/string.h>
25 #include <linux/ctype.h>
26
27 #include <common.h>
28 #include <environment.h>
29
30 #ifdef CONFIG_CMD_NET
31 #define ENV_FLAGS_NET_VARTYPE_REPS "im"
32 #else
33 #define ENV_FLAGS_NET_VARTYPE_REPS ""
34 #endif
35
36 static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
37
38 /*
39  * Parse the flags string from a .flags attribute list into the vartype enum.
40  */
41 enum env_flags_vartype env_flags_parse_vartype(const char *flags)
42 {
43         char *type;
44
45         if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
46                 return env_flags_vartype_string;
47
48         type = strchr(env_flags_vartype_rep,
49                 flags[ENV_FLAGS_VARTYPE_LOC]);
50
51         if (type != NULL)
52                 return (enum env_flags_vartype)
53                         (type - &env_flags_vartype_rep[0]);
54
55         printf("## Warning: Unknown environment variable type '%c'\n",
56                 flags[ENV_FLAGS_VARTYPE_LOC]);
57         return env_flags_vartype_string;
58 }
59
60 static inline int is_hex_prefix(const char *value)
61 {
62         return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
63 }
64
65 static void skip_num(int hex, const char *value, const char **end,
66         int max_digits)
67 {
68         int i;
69
70         if (hex && is_hex_prefix(value))
71                 value += 2;
72
73         for (i = max_digits; i != 0; i--) {
74                 if (hex && !isxdigit(*value))
75                         break;
76                 if (!hex && !isdigit(*value))
77                         break;
78                 value++;
79         }
80         if (end != NULL)
81                 *end = value;
82 }
83
84 /*
85  * Based on the declared type enum, validate that the value string complies
86  * with that format
87  */
88 static int _env_flags_validate_type(const char *value,
89         enum env_flags_vartype type)
90 {
91         const char *end;
92 #ifdef CONFIG_CMD_NET
93         const char *cur;
94         int i;
95 #endif
96
97         switch (type) {
98         case env_flags_vartype_string:
99                 break;
100         case env_flags_vartype_decimal:
101                 skip_num(0, value, &end, -1);
102                 if (*end != '\0')
103                         return -1;
104                 break;
105         case env_flags_vartype_hex:
106                 skip_num(1, value, &end, -1);
107                 if (*end != '\0')
108                         return -1;
109                 if (value + 2 == end && is_hex_prefix(value))
110                         return -1;
111                 break;
112         case env_flags_vartype_bool:
113                 if (value[0] != '1' && value[0] != 'y' && value[0] != 't' &&
114                     value[0] != 'Y' && value[0] != 'T' &&
115                     value[0] != '0' && value[0] != 'n' && value[0] != 'f' &&
116                     value[0] != 'N' && value[0] != 'F')
117                         return -1;
118                 if (value[1] != '\0')
119                         return -1;
120                 break;
121 #ifdef CONFIG_CMD_NET
122         case env_flags_vartype_ipaddr:
123                 cur = value;
124                 for (i = 0; i < 4; i++) {
125                         skip_num(0, cur, &end, 3);
126                         if (cur == end)
127                                 return -1;
128                         if (i != 3 && *end != '.')
129                                 return -1;
130                         if (i == 3 && *end != '\0')
131                                 return -1;
132                         cur = end + 1;
133                 }
134                 break;
135         case env_flags_vartype_macaddr:
136                 cur = value;
137                 for (i = 0; i < 6; i++) {
138                         skip_num(1, cur, &end, 2);
139                         if (cur == end)
140                                 return -1;
141                         if (cur + 2 == end && is_hex_prefix(cur))
142                                 return -1;
143                         if (i != 5 && *end != ':')
144                                 return -1;
145                         if (i == 5 && *end != '\0')
146                                 return -1;
147                         cur = end + 1;
148                 }
149                 break;
150 #endif
151         case env_flags_vartype_end:
152                 return -1;
153         }
154
155         /* OK */
156         return 0;
157 }
158
159 /*
160  * Look for flags in a provided list and failing that the static list
161  */
162 static inline int env_flags_lookup(const char *flags_list, const char *name,
163         char *flags)
164 {
165         int ret = 1;
166
167         if (!flags)
168                 /* bad parameter */
169                 return -1;
170
171         /* try the env first */
172         if (flags_list)
173                 ret = env_attr_lookup(flags_list, name, flags);
174
175         if (ret != 0)
176                 /* if not found in the env, look in the static list */
177                 ret = env_attr_lookup(ENV_FLAGS_LIST_STATIC, name, flags);
178
179         return ret;
180 }
181
182 /*
183  * Parse the flag charachters from the .flags attribute list into the binary
184  * form to be stored in the environment entry->flags field.
185  */
186 static int env_parse_flags_to_bin(const char *flags)
187 {
188         return env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
189 }
190
191 /*
192  * Look for possible flags for a newly added variable
193  * This is called specifically when the variable did not exist in the hash
194  * previously, so the blanket update did not find this variable.
195  */
196 void env_flags_init(ENTRY *var_entry)
197 {
198         const char *var_name = var_entry->key;
199         const char *flags_list = getenv(ENV_FLAGS_VAR);
200         char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = "";
201         int ret = 1;
202
203         /* look in the ".flags" and static for a reference to this variable */
204         ret = env_flags_lookup(flags_list, var_name, flags);
205
206         /* if any flags were found, set the binary form to the entry */
207         if (!ret && strlen(flags))
208                 var_entry->flags = env_parse_flags_to_bin(flags);
209 }
210
211 /*
212  * Called on each existing env var prior to the blanket update since removing
213  * a flag in the flag list should remove its flags.
214  */
215 static int clear_flags(ENTRY *entry)
216 {
217         entry->flags = 0;
218
219         return 0;
220 }
221
222 /*
223  * Call for each element in the list that defines flags for a variable
224  */
225 static int set_flags(const char *name, const char *value)
226 {
227         ENTRY e, *ep;
228
229         e.key   = name;
230         e.data  = NULL;
231         hsearch_r(e, FIND, &ep, &env_htab, 0);
232
233         /* does the env variable actually exist? */
234         if (ep != NULL) {
235                 /* the flag list is empty, so clear the flags */
236                 if (value == NULL || strlen(value) == 0)
237                         ep->flags = 0;
238                 else
239                         /* assign the requested flags */
240                         ep->flags = env_parse_flags_to_bin(value);
241         }
242
243         return 0;
244 }
245
246 static int on_flags(const char *name, const char *value, enum env_op op,
247         int flags)
248 {
249         /* remove all flags */
250         hwalk_r(&env_htab, clear_flags);
251
252         /* configure any static flags */
253         env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags);
254         /* configure any dynamic flags */
255         env_attr_walk(value, set_flags);
256
257         return 0;
258 }
259 U_BOOT_ENV_CALLBACK(flags, on_flags);
260
261 /*
262  * Perform consistency checking before creating, overwriting, or deleting an
263  * environment variable. Called as a callback function by hsearch_r() and
264  * hdelete_r(). Returns 0 in case of success, 1 in case of failure.
265  * When (flag & H_FORCE) is set, do not print out any error message and force
266  * overwriting of write-once variables.
267  */
268
269 int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
270         int flag)
271 {
272         const char *name;
273 #if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \
274 && defined(CONFIG_ETHADDR)
275         const char *oldval = NULL;
276
277         if (op != env_op_create)
278                 oldval = item->data;
279 #endif
280
281         name = item->key;
282
283         /* Default value for NULL to protect string-manipulating functions */
284         newval = newval ? : "";
285
286 #ifndef CONFIG_ENV_OVERWRITE
287         /*
288          * Some variables like "ethaddr" and "serial#" can be set only once and
289          * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined.
290          */
291         if (op != env_op_create &&              /* variable exists */
292                 (flag & H_FORCE) == 0) {        /* and we are not forced */
293                 if (strcmp(name, "serial#") == 0 ||
294                     (strcmp(name, "ethaddr") == 0
295 #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
296                      && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0
297 #endif  /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
298                         )) {
299                         printf("Can't overwrite \"%s\"\n", name);
300                         return 1;
301                 }
302         }
303 #endif
304
305         /* validate the value to match the variable type */
306         if (op != env_op_delete) {
307                 enum env_flags_vartype type = (enum env_flags_vartype)
308                         (ENV_FLAGS_VARTYPE_BIN_MASK & item->flags);
309
310                 if (_env_flags_validate_type(newval, type) < 0) {
311                         printf("## Error: flags type check failure for "
312                                 "\"%s\" <= \"%s\" (type: %c)\n",
313                                 name, newval, env_flags_vartype_rep[type]);
314                         return -1;
315                 }
316         }
317
318         return 0;
319 }