envblk.c 6.35 KB
Newer Older
1
/* envblk.c - Common functions for environment block.  */
2 3
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2008,2009  Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 *  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>
23
#include <grub/mm.h>
24
#include <grub/lib/envblk.h>
25 26

grub_envblk_t
27
grub_envblk_open (char *buf, grub_size_t size)
28
{
29
  grub_envblk_t envblk;
30

31 32 33 34 35 36 37
  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;
    }
38

39 40 41 42 43 44
  envblk = grub_malloc (sizeof (*envblk));
  if (envblk)
    {
      envblk->buf = buf;
      envblk->size = size;
    }
45

46 47
  return envblk;
}
48

49 50 51 52 53 54
void
grub_envblk_close (grub_envblk_t envblk)
{
  grub_free (envblk->buf);
  grub_free (envblk);
}
55

56 57 58 59 60
static int
escaped_value_len (const char *value)
{
  int n = 0;
  char *p;
61

62 63 64 65 66 67 68
  for (p = (char *) value; *p; p++)
    {
      if (*p == '\\' || *p == '\n')
        n += 2;
      else
        n++;
    }
69

70 71
  return n;
}
72

73 74 75 76 77 78 79 80 81 82 83 84
static char *
find_next_line (char *p, const char *pend)
{
  while (p < pend)
    {
      if (*p == '\\')
        p += 2;
      else if (*p == '\n')
        break;
      else
        p++;
    }
85

86
  return p + 1;
87 88 89
}

int
90
grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value)
91 92
{
  char *p, *pend;
93 94
  char *space;
  int found = 0;
95
  int nl;
96 97
  int vl;
  int i;
98 99

  nl = grub_strlen (name);
100 101 102
  vl = escaped_value_len (value);
  p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
  pend = envblk->buf + envblk->size;
103

104 105 106
  /* First, look at free space.  */
  for (space = pend - 1; *space == '#'; space--)
    ;
107

108 109 110
  if (*space != '\n')
    /* Broken.  */
    return 0;
111

112
  space++;
113

114
  while (p + nl + 1 < space)
115
    {
116 117 118
      if (grub_memcmp (p, name, nl) == 0 && p[nl] == '=')
        {
          int len;
119

120 121
          /* Found the same name.  */
          p += nl + 1;
122

123 124 125 126 127 128 129 130 131
          /* Check the length of the current value.  */
          len = 0;
          while (p + len < pend && p[len] != '\n')
            {
              if (p[len] == '\\')
                len += 2;
              else
                len++;
            }
132

133 134 135
          if (p + len >= pend)
            /* Broken.  */
            return 0;
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
          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);
157
    }
158 159

  if (! found)
160
    {
161
      /* Append a new variable.  */
162

163 164 165
      if (pend - space < nl + 1 + vl + 1)
        /* No space.  */
        return 0;
166

167 168 169 170
      grub_memcpy (space, name, nl);
      p = space + nl;
      *p++ = '=';
    }
171

172 173 174 175 176
  /* Write the value.  */
  for (i = 0; value[i]; i++)
    {
      if (value[i] == '\\' || value[i] == '\n')
        *p++ = '\\';
177

178
      *p++ = value[i];
179 180
    }

181 182
  *p = '\n';
  return 1;
183 184 185
}

void
186
grub_envblk_delete (grub_envblk_t envblk, const char *name)
187 188 189 190 191
{
  char *p, *pend;
  int nl;

  nl = grub_strlen (name);
192 193
  p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
  pend = envblk->buf + envblk->size;
194

195
  while (p + nl + 1 < pend)
196
    {
197 198 199 200
      if (grub_memcmp (p, name, nl) == 0 && p[nl] == '=')
        {
          /* Found.  */
          int len = nl + 1;
201

202 203 204 205 206 207 208 209 210
          while (p + len < pend)
            {
              if (p[len] == '\n')
                break;
              else if (p[len] == '\\')
                len += 2;
              else
                len++;
            }
211

212 213 214
          if (p + len >= pend)
            /* Broken.  */
            return;
215

216 217 218 219 220 221 222
          len++;
          grub_memmove (p, p + len, pend - (p + len));
          grub_memset (pend - len, '#', len);
          break;
        }

      p = find_next_line (p, pend);
223
    }
224 225 226 227
}

void
grub_envblk_iterate (grub_envblk_t envblk,
228 229
                     void *hook_data,
                     int hook (const char *name, const char *value, void *hook_data))
230 231 232
{
  char *p, *pend;

233 234
  p = envblk->buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1;
  pend = envblk->buf + envblk->size;
235

236
  while (p < pend)
237
    {
238
      if (*p != '#')
239
        {
240 241 242 243 244
          char *name;
          char *value;
          char *name_start, *name_end, *value_start;
          char *q;
          int ret;
245

246 247 248 249 250 251 252
          name_start = p;
          while (p < pend && *p != '=')
            p++;
          if (p == pend)
            /* Broken.  */
            return;
          name_end = p;
253

254 255 256 257 258 259 260 261 262 263 264
          p++;
          value_start = p;
          while (p < pend)
            {
              if (*p == '\n')
                break;
              else if (*p == '\\')
                p += 2;
              else
                p++;
            }
265

266 267 268 269 270 271 272 273 274 275
          if (p >= pend)
            /* Broken.  */
            return;

          name = grub_malloc (p - name_start + 1);
          if (! name)
            /* out of memory.  */
            return;

          value = name + (value_start - name_start);
276

277 278
          grub_memcpy (name, name_start, name_end - name_start);
          name[name_end - name_start] = '\0';
279

280 281 282 283 284 285 286 287
          for (p = value_start, q = value; *p != '\n'; ++p)
            {
              if (*p == '\\')
                *q++ = *++p;
              else
                *q++ = *p;
            }
          *q = '\0';
288

289
          ret = hook (name, value, hook_data);
290 291 292 293
          grub_free (name);
          if (ret)
            return;
        }
294

295
      p = find_next_line (p, pend);
296 297
    }
}