uboot/env/flags.c
<<
>>
Prefs
   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
  30static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
  31static const char env_flags_varaccess_rep[] = "aroc";
  32static 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
  43static 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};
  53static 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 */
  63void 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 */
  77void 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 */
  91const 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 */
  99const 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 */
 108enum 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 */
 130enum 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 */
 152enum 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
 167static inline int is_hex_prefix(const char *value)
 168{
 169        return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
 170}
 171
 172static 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
 192int 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 */
 220static 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 */
 283static 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 */
 308enum 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 */
 325enum 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 */
 343int 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 */
 363int 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 */
 377int 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 */
 403static 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
 413static int first_call = 1;
 414static 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 */
 421void 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 */
 443static 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 */
 453static 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
 475static 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}
 488U_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
 498int 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