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 <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_CMD_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
  37static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
  38static const char env_flags_varaccess_rep[] =
  39        "aroc" ENV_FLAGS_WRITEABLE_VARACCESS_REPS;
  40static 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
  55static const char * const env_flags_vartype_names[] = {
  56        "string",
  57        "decimal",
  58        "hexadecimal",
  59        "boolean",
  60#ifdef CONFIG_CMD_NET
  61        "IP address",
  62        "MAC address",
  63#endif
  64};
  65static 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 */
  78void 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 */
  92void 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 */
 106const 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 */
 114const 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 */
 123enum 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 */
 145enum 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 */
 171enum 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
 190static inline int is_hex_prefix(const char *value)
 191{
 192        return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
 193}
 194
 195static 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_CMD_NET
 215int 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 */
 243static int _env_flags_validate_type(const char *value,
 244        enum env_flags_vartype type)
 245{
 246        const char *end;
 247#ifdef CONFIG_CMD_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_CMD_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 */
 306static 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 */
 331enum 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 */
 348enum 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 */
 367int 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 */
 387int 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 */
 401int 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 */
 427static 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
 437static int first_call = 1;
 438static 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 */
 445void 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 */
 471static 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 */
 481static 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
 502static 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}
 515U_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
 525int 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
 610