~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/commands/wildcard.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* wildcard.c - Wildcard character expansion for GRUB script.  */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2010  Free Software Foundation, Inc.
 
5
 *
 
6
 *  GRUB is free software: you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation, either version 3 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  GRUB is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <grub/mm.h>
 
21
#include <grub/fs.h>
 
22
#include <grub/env.h>
 
23
#include <grub/file.h>
 
24
#include <grub/device.h>
 
25
#include <grub/script_sh.h>
 
26
 
 
27
#include <regex.h>
 
28
 
 
29
static inline int isregexop (char ch);
 
30
static char ** merge (char **lhs, char **rhs);
 
31
static char *make_dir (const char *prefix, const char *start, const char *end);
 
32
static int make_regex (const char *regex_start, const char *regex_end,
 
33
                       regex_t *regexp);
 
34
static void split_path (const char *path, const char **suffix_end, const char **regex_end);
 
35
static char ** match_devices (const regex_t *regexp, int noparts);
 
36
static char ** match_files (const char *prefix, const char *suffix_start,
 
37
                            const char *suffix_end, const regex_t *regexp);
 
38
 
 
39
static char* wildcard_escape (const char *s);
 
40
static char* wildcard_unescape (const char *s);
 
41
static grub_err_t wildcard_expand (const char *s, char ***strs);
 
42
 
 
43
struct grub_script_wildcard_translator grub_filename_translator = {
 
44
  .expand = wildcard_expand,
 
45
  .escape = wildcard_escape,
 
46
  .unescape = wildcard_unescape
 
47
};
 
48
 
 
49
static char **
 
50
merge (char **dest, char **ps)
 
51
{
 
52
  int i;
 
53
  int j;
 
54
  char **p;
 
55
 
 
56
  if (! dest)
 
57
    return ps;
 
58
 
 
59
  if (! ps)
 
60
    return dest;
 
61
 
 
62
  for (i = 0; dest[i]; i++)
 
63
    ;
 
64
  for (j = 0; ps[j]; j++)
 
65
    ;
 
66
 
 
67
  p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
 
68
  if (! p)
 
69
    {
 
70
      grub_free (dest);
 
71
      grub_free (ps);
 
72
      return 0;
 
73
    }
 
74
 
 
75
  dest = p;
 
76
  for (j = 0; ps[j]; j++)
 
77
    dest[i++] = ps[j];
 
78
  dest[i] = 0;
 
79
 
 
80
  grub_free (ps);
 
81
  return dest;
 
82
}
 
83
 
 
84
static inline int
 
85
isregexop (char ch)
 
86
{
 
87
  return grub_strchr ("*.\\", ch) ? 1 : 0;
 
88
}
 
89
 
 
90
static char *
 
91
make_dir (const char *prefix, const char *start, const char *end)
 
92
{
 
93
  char ch;
 
94
  unsigned i;
 
95
  unsigned n;
 
96
  char *result;
 
97
 
 
98
  i = grub_strlen (prefix);
 
99
  n = i + end - start;
 
100
 
 
101
  result = grub_malloc (n + 1);
 
102
  if (! result)
 
103
    return 0;
 
104
 
 
105
  grub_strcpy (result, prefix);
 
106
  while (start < end && (ch = *start++))
 
107
    if (ch == '\\' && isregexop (*start))
 
108
      result[i++] = *start++;
 
109
    else
 
110
      result[i++] = ch;
 
111
 
 
112
  result[i] = '\0';
 
113
  return result;
 
114
}
 
115
 
 
116
static int
 
117
make_regex (const char *start, const char *end, regex_t *regexp)
 
118
{
 
119
  char ch;
 
120
  int i = 0;
 
121
  unsigned len = end - start;
 
122
  char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
 
123
 
 
124
  if (! buffer)
 
125
    return 1;
 
126
 
 
127
  buffer[i++] = '^';
 
128
  while (start < end)
 
129
    {
 
130
      /* XXX Only * expansion for now.  */
 
131
      switch ((ch = *start++))
 
132
        {
 
133
        case '\\':
 
134
          buffer[i++] = ch;
 
135
          if (*start != '\0')
 
136
            buffer[i++] = *start++;
 
137
          break;
 
138
 
 
139
        case '.':
 
140
        case '(':
 
141
        case ')':
 
142
          buffer[i++] = '\\';
 
143
          buffer[i++] = ch;
 
144
          break;
 
145
 
 
146
        case '*':
 
147
          buffer[i++] = '.';
 
148
          buffer[i++] = '*';
 
149
          break;
 
150
 
 
151
        default:
 
152
          buffer[i++] = ch;
 
153
        }
 
154
    }
 
155
  buffer[i++] = '$';
 
156
  buffer[i] = '\0';
 
157
  grub_dprintf ("expand", "Regexp is %s\n", buffer);
 
158
 
 
159
  if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK))
 
160
    {
 
161
      grub_free (buffer);
 
162
      return 1;
 
163
    }
 
164
 
 
165
  grub_free (buffer);
 
166
  return 0;
 
167
}
 
168
 
 
169
/* Split `str' into two parts: (1) dirname that is regexop free (2)
 
170
   dirname that has a regexop.  */
 
171
static void
 
172
split_path (const char *str, const char **noregexop, const char **regexop)
 
173
{
 
174
  char ch = 0;
 
175
  int regex = 0;
 
176
 
 
177
  const char *end;
 
178
  const char *split;  /* points till the end of dirnaname that doesn't
 
179
                         need expansion.  */
 
180
 
 
181
  split = end = str;
 
182
  while ((ch = *end))
 
183
    {
 
184
      if (ch == '\\' && end[1])
 
185
        end++;
 
186
 
 
187
      else if (isregexop (ch))
 
188
        regex = 1;
 
189
 
 
190
      else if (ch == '/' && ! regex)
 
191
        split = end + 1;  /* forward to next regexop-free dirname */
 
192
 
 
193
      else if (ch == '/' && regex)
 
194
        break;  /* stop at the first dirname with a regexop */
 
195
 
 
196
      end++;
 
197
    }
 
198
 
 
199
  *regexop = end;
 
200
  if (! regex)
 
201
    *noregexop = end;
 
202
  else
 
203
    *noregexop = split;
 
204
}
 
205
 
 
206
static char **
 
207
match_devices (const regex_t *regexp, int noparts)
 
208
{
 
209
  int i;
 
210
  int ndev;
 
211
  char **devs;
 
212
 
 
213
  auto int match (const char *name);
 
214
  int match (const char *name)
 
215
  {
 
216
    char **t;
 
217
    char *buffer;
 
218
 
 
219
    /* skip partitions if asked to. */
 
220
    if (noparts && grub_strchr(name, ','))
 
221
      return 0;
 
222
 
 
223
    buffer = grub_xasprintf ("(%s)", name);
 
224
    if (! buffer)
 
225
      return 1;
 
226
 
 
227
    grub_dprintf ("expand", "matching: %s\n", buffer);
 
228
    if (regexec (regexp, buffer, 0, 0, 0))
 
229
      {
 
230
        grub_dprintf ("expand", "not matched\n");
 
231
        grub_free (buffer);
 
232
        return 0;
 
233
      }
 
234
 
 
235
    t = grub_realloc (devs, sizeof (char*) * (ndev + 2));
 
236
    if (! t)
 
237
      return 1;
 
238
 
 
239
    devs = t;
 
240
    devs[ndev++] = buffer;
 
241
    devs[ndev] = 0;
 
242
    return 0;
 
243
  }
 
244
 
 
245
  ndev = 0;
 
246
  devs = 0;
 
247
 
 
248
  if (grub_device_iterate (match))
 
249
    goto fail;
 
250
 
 
251
  return devs;
 
252
 
 
253
 fail:
 
254
 
 
255
  for (i = 0; devs && devs[i]; i++)
 
256
    grub_free (devs[i]);
 
257
 
 
258
  if (devs)
 
259
    grub_free (devs);
 
260
 
 
261
  return 0;
 
262
}
 
263
 
 
264
static char **
 
265
match_files (const char *prefix, const char *suffix, const char *end,
 
266
             const regex_t *regexp)
 
267
{
 
268
  int i;
 
269
  int error;
 
270
  char **files;
 
271
  unsigned nfile;
 
272
  char *dir;
 
273
  const char *path;
 
274
  char *device_name;
 
275
  grub_fs_t fs;
 
276
  grub_device_t dev;
 
277
 
 
278
  auto int match (const char *name, const struct grub_dirhook_info *info);
 
279
  int match (const char *name, const struct grub_dirhook_info *info)
 
280
  {
 
281
    char **t;
 
282
    char *buffer;
 
283
 
 
284
    /* skip . and .. names */
 
285
    if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
 
286
      return 0;
 
287
 
 
288
    grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
 
289
    if (regexec (regexp, name, 0, 0, 0))
 
290
      return 0;
 
291
 
 
292
    buffer = grub_xasprintf ("%s%s", dir, name);
 
293
    if (! buffer)
 
294
      return 1;
 
295
 
 
296
    t = grub_realloc (files, sizeof (char*) * (nfile + 2));
 
297
    if (! t)
 
298
      {
 
299
        grub_free (buffer);
 
300
        return 1;
 
301
      }
 
302
 
 
303
    files = t;
 
304
    files[nfile++] = buffer;
 
305
    files[nfile] = 0;
 
306
    return 0;
 
307
  }
 
308
 
 
309
  nfile = 0;
 
310
  files = 0;
 
311
  dev = 0;
 
312
  device_name = 0;
 
313
  grub_error_push ();
 
314
 
 
315
  dir = make_dir (prefix, suffix, end);
 
316
  if (! dir)
 
317
    goto fail;
 
318
 
 
319
  device_name = grub_file_get_device_name (dir);
 
320
  dev = grub_device_open (device_name);
 
321
  if (! dev)
 
322
    goto fail;
 
323
 
 
324
  fs = grub_fs_probe (dev);
 
325
  if (! fs)
 
326
    goto fail;
 
327
 
 
328
  path = grub_strchr (dir, ')');
 
329
  if (! path)
 
330
    goto fail;
 
331
  path++;
 
332
 
 
333
  if (fs->dir (dev, path, match))
 
334
    goto fail;
 
335
 
 
336
  grub_free (dir);
 
337
  grub_device_close (dev);
 
338
  grub_free (device_name);
 
339
  grub_error_pop ();
 
340
  return files;
 
341
 
 
342
 fail:
 
343
 
 
344
  if (dir)
 
345
    grub_free (dir);
 
346
 
 
347
  for (i = 0; files && files[i]; i++)
 
348
    grub_free (files[i]);
 
349
 
 
350
  if (files)
 
351
    grub_free (files);
 
352
 
 
353
  if (dev)
 
354
    grub_device_close (dev);
 
355
 
 
356
  if (device_name)
 
357
    grub_free (device_name);
 
358
 
 
359
  grub_error_pop ();
 
360
  return 0;
 
361
}
 
362
 
 
363
static char*
 
364
wildcard_escape (const char *s)
 
365
{
 
366
  int i;
 
367
  int len;
 
368
  char ch;
 
369
  char *p;
 
370
 
 
371
  len = grub_strlen (s);
 
372
  p = grub_malloc (len * 2 + 1);
 
373
  if (! p)
 
374
    return NULL;
 
375
 
 
376
  i = 0;
 
377
  while ((ch = *s++))
 
378
    {
 
379
      if (isregexop (ch))
 
380
        p[i++] = '\\';
 
381
      p[i++] = ch;
 
382
    }
 
383
  p[i] = '\0';
 
384
  return p;
 
385
}
 
386
 
 
387
static char*
 
388
wildcard_unescape (const char *s)
 
389
{
 
390
  int i;
 
391
  int len;
 
392
  char ch;
 
393
  char *p;
 
394
 
 
395
  len = grub_strlen (s);
 
396
  p = grub_malloc (len + 1);
 
397
  if (! p)
 
398
    return NULL;
 
399
 
 
400
  i = 0;
 
401
  while ((ch = *s++))
 
402
    {
 
403
      if (ch == '\\' && isregexop (*s))
 
404
        p[i++] = *s++;
 
405
      else
 
406
        p[i++] = ch;
 
407
    }
 
408
  p[i] = '\0';
 
409
  return p;
 
410
}
 
411
 
 
412
static grub_err_t
 
413
wildcard_expand (const char *s, char ***strs)
 
414
{
 
415
  const char *start;
 
416
  const char *regexop;
 
417
  const char *noregexop;
 
418
  char **paths = 0;
 
419
 
 
420
  unsigned i;
 
421
  regex_t regexp;
 
422
 
 
423
  start = s;
 
424
  while (*start)
 
425
    {
 
426
      split_path (start, &noregexop, &regexop);
 
427
      if (noregexop >= regexop) /* no more wildcards */
 
428
        break;
 
429
 
 
430
      if (make_regex (noregexop, regexop, &regexp))
 
431
        goto fail;
 
432
 
 
433
      if (paths == 0)
 
434
        {
 
435
          if (start == noregexop) /* device part has regexop */
 
436
            paths = match_devices (&regexp, *start != '(');
 
437
 
 
438
          else if (*start == '(') /* device part explicit wo regexop */
 
439
            paths = match_files ("", start, noregexop, &regexp);
 
440
 
 
441
          else if (*start == '/') /* no device part */
 
442
            {
 
443
              char **r;
 
444
              unsigned n;
 
445
              char *root;
 
446
              char *prefix;
 
447
 
 
448
              root = grub_env_get ("root");
 
449
              if (! root)
 
450
                goto fail;
 
451
 
 
452
              prefix = grub_xasprintf ("(%s)", root);
 
453
              if (! prefix)
 
454
                goto fail;
 
455
 
 
456
              paths = match_files (prefix, start, noregexop, &regexp);
 
457
              grub_free (prefix);
 
458
            }
 
459
        }
 
460
      else
 
461
        {
 
462
          char **r = 0;
 
463
 
 
464
          for (i = 0; paths[i]; i++)
 
465
            {
 
466
              char **p;
 
467
 
 
468
              p = match_files (paths[i], start, noregexop, &regexp);
 
469
              if (! p)
 
470
                continue;
 
471
 
 
472
              r = merge (r, p);
 
473
              if (! r)
 
474
                goto fail;
 
475
            }
 
476
          paths = r;
 
477
        }
 
478
 
 
479
      regfree (&regexp);
 
480
      if (! paths)
 
481
        goto done;
 
482
 
 
483
      start = regexop;
 
484
    }
 
485
 
 
486
 done:
 
487
 
 
488
  *strs = paths;
 
489
  return 0;
 
490
 
 
491
 fail:
 
492
 
 
493
  for (i = 0; paths && paths[i]; i++)
 
494
    grub_free (paths[i]);
 
495
  grub_free (paths);
 
496
  regfree (&regexp);
 
497
  return grub_errno;
 
498
}