grub/grub-core/lib/envblk.c
Shreenidhi Shedi be303f8c18 lib/envblk: Ignore empty new lines while parsing env files
Environment files may contain empty lines, which should be ignored
during parsing. Currently, these lines are not skipped and resulting in
incorrect behavior. This patch adds a check to skip empty lines along
with those starting with "#".

Signed-off-by: Shreenidhi Shedi <shreenidhi.shedi@broadcom.com>
Reviewed-by: Alexey Makhalov <alexey.makhalov@broadcom.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2025-05-29 15:45:09 +02:00

298 lines
6.4 KiB
C

/* envblk.c - Common functions for environment block. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/lib/envblk.h>
grub_envblk_t
grub_envblk_open (char *buf, grub_size_t size)
{
grub_envblk_t envblk;
if (size < sizeof (GRUB_ENVBLK_SIGNATURE)
|| grub_memcmp (buf, GRUB_ENVBLK_SIGNATURE,
sizeof (GRUB_ENVBLK_SIGNATURE) - 1))
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
return 0;
}
envblk = grub_malloc (sizeof (*envblk));
if (envblk)
{
envblk->buf = buf;
envblk->size = size;
}
return envblk;
}
void
grub_envblk_close (grub_envblk_t envblk)
{
grub_free (envblk->buf);
grub_free (envblk);
}
static int
escaped_value_len (const char *value)
{
int n = 0;
char *p;
for (p = (char *) value; *p; p++)
{
if (*p == '\\' || *p == '\n')
n += 2;
else
n++;
}
return n;
}
static char *
find_next_line (char *p, const char *pend)
{
while (p < pend)
{
if (*p == '\\')
p += 2;
else if (*p == '\n')
break;
else
p++;
}
return p + 1;
}
int
grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value)
{
char *p, *pend;
char *space;
int found = 0;
int nl;
int vl;
int i;
nl = grub_strlen (name);
vl = escaped_value_len (value);
p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
pend = envblk->buf + envblk->size;
/* First, look at free space. */
for (space = pend - 1; *space == '#'; space--)
;
if (*space != '\n')
/* Broken. */
return 0;
space++;
while (p + nl + 1 < space)
{
if (grub_memcmp (p, name, nl) == 0 && p[nl] == '=')
{
int len;
/* Found the same name. */
p += nl + 1;
/* Check the length of the current value. */
len = 0;
while (p + len < pend && p[len] != '\n')
{
if (p[len] == '\\')
len += 2;
else
len++;
}
if (p + len >= pend)
/* Broken. */
return 0;
if (pend - space < vl - len)
/* No space. */
return 0;
if (vl < len)
{
/* Move the following characters backward, and fill the new
space with harmless characters. */
grub_memmove (p + vl, p + len, pend - (p + len));
grub_memset (space - (len - vl), '#', len - vl);
}
else
/* Move the following characters forward. */
grub_memmove (p + vl, p + len, pend - (p + vl));
found = 1;
break;
}
p = find_next_line (p, pend);
}
if (! found)
{
/* Append a new variable. */
if (pend - space < nl + 1 + vl + 1)
/* No space. */
return 0;
grub_memcpy (space, name, nl);
p = space + nl;
*p++ = '=';
}
/* Write the value. */
for (i = 0; value[i]; i++)
{
if (value[i] == '\\' || value[i] == '\n')
*p++ = '\\';
*p++ = value[i];
}
*p = '\n';
return 1;
}
void
grub_envblk_delete (grub_envblk_t envblk, const char *name)
{
char *p, *pend;
int nl;
nl = grub_strlen (name);
p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
pend = envblk->buf + envblk->size;
while (p + nl + 1 < pend)
{
if (grub_memcmp (p, name, nl) == 0 && p[nl] == '=')
{
/* Found. */
int len = nl + 1;
while (p + len < pend)
{
if (p[len] == '\n')
break;
else if (p[len] == '\\')
len += 2;
else
len++;
}
if (p + len >= pend)
/* Broken. */
return;
len++;
grub_memmove (p, p + len, pend - (p + len));
grub_memset (pend - len, '#', len);
break;
}
p = find_next_line (p, pend);
}
}
void
grub_envblk_iterate (grub_envblk_t envblk,
void *hook_data,
int hook (const char *name, const char *value, void *hook_data))
{
char *p, *pend;
p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
pend = envblk->buf + envblk->size;
while (p < pend)
{
if (*p != '#' && *p != '\n' && *p != '\r')
{
char *name;
char *value;
char *name_start, *name_end, *value_start;
char *q;
int ret;
name_start = p;
while (p < pend && *p != '=')
p++;
if (p == pend)
/* Broken. */
return;
name_end = p;
p++;
value_start = p;
while (p < pend)
{
if (*p == '\n')
break;
else if (*p == '\\')
p += 2;
else
p++;
}
if (p >= pend)
/* Broken. */
return;
name = grub_malloc (p - name_start + 1);
if (! name)
/* out of memory. */
return;
value = name + (value_start - name_start);
grub_memcpy (name, name_start, name_end - name_start);
name[name_end - name_start] = '\0';
for (p = value_start, q = value; *p != '\n'; ++p)
{
if (*p == '\\')
*q++ = *++p;
else
*q++ = *p;
}
*q = '\0';
ret = hook (name, value, hook_data);
grub_free (name);
if (ret)
return;
}
p = find_next_line (p, pend);
}
}