/* 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, int hook (const char *name, const char *value)) { char *p, *pend; p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1; pend = envblk->buf + envblk->size; while (p < pend) { if (*p != '#') { 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); grub_free (name); if (ret) return; } p = find_next_line (p, pend); } }