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