source: grub-pc/trunk/fuentes/grub-core/commands/wildcard.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 11.7 KB
Line 
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
29static inline int isregexop (char ch);
30static char ** merge (char **lhs, char **rhs);
31static char *make_dir (const char *prefix, const char *start, const char *end);
32static int make_regex (const char *regex_start, const char *regex_end,
33                       regex_t *regexp);
34static void split_path (const char *path, const char **suffix_end, const char **regex_end);
35static char ** match_devices (const regex_t *regexp, int noparts);
36static char ** match_files (const char *prefix, const char *suffix_start,
37                            const char *suffix_end, const regex_t *regexp);
38
39static grub_err_t wildcard_expand (const char *s, char ***strs);
40
41struct grub_script_wildcard_translator grub_filename_translator = {
42  .expand = wildcard_expand,
43};
44
45static char **
46merge (char **dest, char **ps)
47{
48  int i;
49  int j;
50  char **p;
51
52  if (! dest)
53    return ps;
54
55  if (! ps)
56    return dest;
57
58  for (i = 0; dest[i]; i++)
59    ;
60  for (j = 0; ps[j]; j++)
61    ;
62
63  p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
64  if (! p)
65    {
66      grub_free (dest);
67      grub_free (ps);
68      return 0;
69    }
70
71  dest = p;
72  for (j = 0; ps[j]; j++)
73    dest[i++] = ps[j];
74  dest[i] = 0;
75
76  grub_free (ps);
77  return dest;
78}
79
80static inline int
81isregexop (char ch)
82{
83  return grub_strchr ("*.\\|+{}[]?", ch) ? 1 : 0;
84}
85
86static char *
87make_dir (const char *prefix, const char *start, const char *end)
88{
89  char ch;
90  unsigned i;
91  unsigned n;
92  char *result;
93
94  i = grub_strlen (prefix);
95  n = i + end - start;
96
97  result = grub_malloc (n + 1);
98  if (! result)
99    return 0;
100
101  grub_strcpy (result, prefix);
102  while (start < end && (ch = *start++))
103    if (ch == '\\' && isregexop (*start))
104      result[i++] = *start++;
105    else
106      result[i++] = ch;
107
108  result[i] = '\0';
109  return result;
110}
111
112static int
113make_regex (const char *start, const char *end, regex_t *regexp)
114{
115  char ch;
116  int i = 0;
117  unsigned len = end - start;
118  char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
119
120  if (! buffer)
121    return 1;
122
123  buffer[i++] = '^';
124  while (start < end)
125    {
126      /* XXX Only * and ? expansion for now.  */
127      switch ((ch = *start++))
128        {
129        case '\\':
130          buffer[i++] = ch;
131          if (*start != '\0')
132            buffer[i++] = *start++;
133          break;
134
135        case '.':
136        case '(':
137        case ')':
138        case '@':
139        case '+':
140        case '|':
141        case '{':
142        case '}':
143        case '[':
144        case ']':
145          buffer[i++] = '\\';
146          buffer[i++] = ch;
147          break;
148
149        case '*':
150          buffer[i++] = '.';
151          buffer[i++] = '*';
152          break;
153
154        case '?':
155          buffer[i++] = '.';
156          break;
157
158        default:
159          buffer[i++] = ch;
160        }
161    }
162  buffer[i++] = '$';
163  buffer[i] = '\0';
164  grub_dprintf ("expand", "Regexp is %s\n", buffer);
165
166  if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK))
167    {
168      grub_free (buffer);
169      return 1;
170    }
171
172  grub_free (buffer);
173  return 0;
174}
175
176/* Split `str' into two parts: (1) dirname that is regexop free (2)
177   dirname that has a regexop.  */
178static void
179split_path (const char *str, const char **noregexop, const char **regexop)
180{
181  char ch = 0;
182  int regex = 0;
183
184  const char *end;
185  const char *split;  /* points till the end of dirnaname that doesn't
186                         need expansion.  */
187
188  split = end = str;
189  while ((ch = *end))
190    {
191      if (ch == '\\' && end[1])
192        end++;
193
194      else if (ch == '*' || ch == '?')
195        regex = 1;
196
197      else if (ch == '/' && ! regex)
198        split = end + 1;  /* forward to next regexop-free dirname */
199
200      else if (ch == '/' && regex)
201        break;  /* stop at the first dirname with a regexop */
202
203      end++;
204    }
205
206  *regexop = end;
207  if (! regex)
208    *noregexop = end;
209  else
210    *noregexop = split;
211}
212
213/* Context for match_devices.  */
214struct match_devices_ctx
215{
216  const regex_t *regexp;
217  int noparts;
218  int ndev;
219  char **devs;
220};
221
222/* Helper for match_devices.  */
223static int
224match_devices_iter (const char *name, void *data)
225{
226  struct match_devices_ctx *ctx = data;
227  char **t;
228  char *buffer;
229
230  /* skip partitions if asked to. */
231  if (ctx->noparts && grub_strchr (name, ','))
232    return 0;
233
234  buffer = grub_xasprintf ("(%s)", name);
235  if (! buffer)
236    return 1;
237
238  grub_dprintf ("expand", "matching: %s\n", buffer);
239  if (regexec (ctx->regexp, buffer, 0, 0, 0))
240    {
241      grub_dprintf ("expand", "not matched\n");
242      grub_free (buffer);
243      return 0;
244    }
245
246  t = grub_realloc (ctx->devs, sizeof (char*) * (ctx->ndev + 2));
247  if (! t)
248    return 1;
249
250  ctx->devs = t;
251  ctx->devs[ctx->ndev++] = buffer;
252  ctx->devs[ctx->ndev] = 0;
253  return 0;
254}
255
256static char **
257match_devices (const regex_t *regexp, int noparts)
258{
259  struct match_devices_ctx ctx = {
260    .regexp = regexp,
261    .noparts = noparts,
262    .ndev = 0,
263    .devs = 0
264  };
265  int i;
266
267  if (grub_device_iterate (match_devices_iter, &ctx))
268    goto fail;
269
270  return ctx.devs;
271
272 fail:
273
274  for (i = 0; ctx.devs && ctx.devs[i]; i++)
275    grub_free (ctx.devs[i]);
276
277  grub_free (ctx.devs);
278
279  return 0;
280}
281
282/* Context for match_files.  */
283struct match_files_ctx
284{
285  const regex_t *regexp;
286  char **files;
287  unsigned nfile;
288  char *dir;
289};
290
291/* Helper for match_files.  */
292static int
293match_files_iter (const char *name, const struct grub_dirhook_info *info,
294                  void *data)
295{
296  struct match_files_ctx *ctx = data;
297  char **t;
298  char *buffer;
299
300  /* skip . and .. names */
301  if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
302    return 0;
303
304  grub_dprintf ("expand", "matching: %s in %s\n", name, ctx->dir);
305  if (regexec (ctx->regexp, name, 0, 0, 0))
306    return 0;
307
308  grub_dprintf ("expand", "matched\n");
309
310  buffer = grub_xasprintf ("%s%s", ctx->dir, name);
311  if (! buffer)
312    return 1;
313
314  t = grub_realloc (ctx->files, sizeof (char*) * (ctx->nfile + 2));
315  if (! t)
316    {
317      grub_free (buffer);
318      return 1;
319    }
320
321  ctx->files = t;
322  ctx->files[ctx->nfile++] = buffer;
323  ctx->files[ctx->nfile] = 0;
324  return 0;
325}
326
327static char **
328match_files (const char *prefix, const char *suffix, const char *end,
329             const regex_t *regexp)
330{
331  struct match_files_ctx ctx = {
332    .regexp = regexp,
333    .nfile = 0,
334    .files = 0
335  };
336  int i;
337  const char *path;
338  char *device_name;
339  grub_fs_t fs;
340  grub_device_t dev;
341
342  dev = 0;
343  device_name = 0;
344  grub_error_push ();
345
346  ctx.dir = make_dir (prefix, suffix, end);
347  if (! ctx.dir)
348    goto fail;
349
350  device_name = grub_file_get_device_name (ctx.dir);
351  dev = grub_device_open (device_name);
352  if (! dev)
353    goto fail;
354
355  fs = grub_fs_probe (dev);
356  if (! fs)
357    goto fail;
358
359  if (ctx.dir[0] == '(')
360    {
361      path = grub_strchr (ctx.dir, ')');
362      if (!path)
363        goto fail;
364      path++;
365    }
366  else
367    path = ctx.dir;
368
369  if (fs->dir (dev, path, match_files_iter, &ctx))
370    goto fail;
371
372  grub_free (ctx.dir);
373  grub_device_close (dev);
374  grub_free (device_name);
375  grub_error_pop ();
376  return ctx.files;
377
378 fail:
379
380  grub_free (ctx.dir);
381
382  for (i = 0; ctx.files && ctx.files[i]; i++)
383    grub_free (ctx.files[i]);
384
385  grub_free (ctx.files);
386
387  if (dev)
388    grub_device_close (dev);
389
390  grub_free (device_name);
391
392  grub_error_pop ();
393  return 0;
394}
395
396/* Context for check_file.  */
397struct check_file_ctx
398{
399  const char *basename;
400  int found;
401};
402
403/* Helper for check_file.  */
404static int
405check_file_iter (const char *name, const struct grub_dirhook_info *info,
406                 void *data)
407{
408  struct check_file_ctx *ctx = data;
409
410  if (ctx->basename[0] == 0
411      || (info->case_insensitive ? grub_strcasecmp (name, ctx->basename) == 0
412          : grub_strcmp (name, ctx->basename) == 0))
413    {
414      ctx->found = 1;
415      return 1;
416    }
417 
418  return 0;
419}
420
421static int
422check_file (const char *dir, const char *basename)
423{
424  struct check_file_ctx ctx = {
425    .basename = basename,
426    .found = 0
427  };
428  grub_fs_t fs;
429  grub_device_t dev;
430  const char *device_name, *path;
431
432  device_name = grub_file_get_device_name (dir);
433  dev = grub_device_open (device_name);
434  if (! dev)
435    goto fail;
436
437  fs = grub_fs_probe (dev);
438  if (! fs)
439    goto fail;
440
441  if (dir[0] == '(')
442    {
443      path = grub_strchr (dir, ')');
444      if (!path)
445        goto fail;
446      path++;
447    }
448  else
449    path = dir;
450
451  fs->dir (dev, path[0] ? path : "/", check_file_iter, &ctx);
452  if (grub_errno == 0 && basename[0] == 0)
453    ctx.found = 1;
454
455 fail:
456  grub_errno = 0;
457
458  return ctx.found;
459}
460
461static void
462unescape (char *out, const char *in, const char *end)
463{
464  char *optr;
465  const char *iptr;
466
467  for (optr = out, iptr = in; iptr < end;)
468    {
469      if (*iptr == '\\' && iptr + 1 < end)
470        {
471          *optr++ = iptr[1];
472          iptr += 2;
473          continue;
474        }
475      if (*iptr == '\\')
476        break;
477      *optr++ = *iptr++;
478    }
479  *optr = 0;
480}
481
482static grub_err_t
483wildcard_expand (const char *s, char ***strs)
484{
485  const char *start;
486  const char *regexop;
487  const char *noregexop;
488  char **paths = 0;
489  int had_regexp = 0;
490
491  unsigned i;
492  regex_t regexp;
493
494  *strs = 0;
495  if (s[0] != '/' && s[0] != '(' && s[0] != '*')
496    return 0;
497
498  start = s;
499  while (*start)
500    {
501      split_path (start, &noregexop, &regexop);
502
503      if (noregexop == regexop)
504        {
505          grub_dprintf ("expand", "no expansion needed\n");
506          if (paths == 0)
507            {
508              paths = grub_malloc (sizeof (char *) * 2);
509              if (!paths)
510                goto fail;
511              paths[0] = grub_malloc (regexop - start + 1);
512              if (!paths[0])
513                goto fail;
514              unescape (paths[0], start, regexop);
515              paths[1] = 0;
516            }
517          else
518            {
519              int j = 0;
520              for (i = 0; paths[i]; i++)
521                {
522                  char *o, *oend;
523                  char *n;
524                  char *p;
525                  o = paths[i];
526                  oend = o + grub_strlen (o);
527                  n = grub_malloc ((oend - o) + (regexop - start) + 1);
528                  if (!n)
529                    goto fail;
530                  grub_memcpy (n, o, oend - o);
531
532                  unescape (n + (oend - o), start, regexop);
533                  if (had_regexp)
534                    p = grub_strrchr (n, '/');
535                  else
536                    p = 0;
537                  if (!p)
538                    {
539                      grub_free (o);
540                      paths[j++] = n;
541                      continue;
542                    }
543                  *p = 0;
544                  if (!check_file (n, p + 1))
545                    {
546                      grub_dprintf ("expand", "file <%s> in <%s> not found\n",
547                                    p + 1, n);
548                      grub_free (o);
549                      grub_free (n);
550                              continue;
551                    }
552                  *p = '/';
553                  grub_free (o);
554                  paths[j++] = n;
555                }
556              if (j == 0)
557                {
558                  grub_free (paths);
559                  paths = 0;
560                  goto done;
561                }
562              paths[j] = 0;
563            }
564          grub_dprintf ("expand", "paths[0] = `%s'\n", paths[0]);
565          start = regexop;
566          continue;
567        }
568
569      if (make_regex (noregexop, regexop, &regexp))
570        goto fail;
571
572      had_regexp = 1;
573
574      if (paths == 0)
575        {
576          if (start == noregexop) /* device part has regexop */
577            paths = match_devices (&regexp, *start != '(');
578
579          else  /* device part explicit wo regexop */
580            paths = match_files ("", start, noregexop, &regexp);
581        }
582      else
583        {
584          char **r = 0;
585
586          for (i = 0; paths[i]; i++)
587            {
588              char **p;
589
590              p = match_files (paths[i], start, noregexop, &regexp);
591              grub_free (paths[i]);
592              if (! p)
593                continue;
594
595              r = merge (r, p);
596              if (! r)
597                goto fail;
598            }
599          grub_free (paths);
600          paths = r;
601        }
602
603      regfree (&regexp);
604      if (! paths)
605        goto done;
606
607      start = regexop;
608    }
609
610 done:
611
612  *strs = paths;
613  return 0;
614
615 fail:
616
617  for (i = 0; paths && paths[i]; i++)
618    grub_free (paths[i]);
619  grub_free (paths);
620  regfree (&regexp);
621  return grub_errno;
622}
Note: See TracBrowser for help on using the repository browser.