~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to grub-core/fs/fshelp.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* fshelp.c -- Filesystem helper functions */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2004,2005,2006,2007,2008  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/err.h>
 
21
#include <grub/mm.h>
 
22
#include <grub/misc.h>
 
23
#include <grub/disk.h>
 
24
#include <grub/fshelp.h>
 
25
 
 
26
 
 
27
/* Lookup the node PATH.  The node ROOTNODE describes the root of the
 
28
   directory tree.  The node found is returned in FOUNDNODE, which is
 
29
   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
 
30
   iterate over all directory entries in the current node.
 
31
   READ_SYMLINK is used to read the symlink if a node is a symlink.
 
32
   EXPECTTYPE is the type node that is expected by the called, an
 
33
   error is generated if the node is not of the expected type.  Make
 
34
   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
 
35
   because GCC has a nasty bug when using regparm=3.  */
 
36
grub_err_t
 
37
grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
 
38
                       grub_fshelp_node_t *foundnode,
 
39
                       int (*iterate_dir) (grub_fshelp_node_t dir,
 
40
                                           int NESTED_FUNC_ATTR (*hook)
 
41
                                           (const char *filename,
 
42
                                            enum grub_fshelp_filetype filetype,
 
43
                                            grub_fshelp_node_t node)),
 
44
                       char *(*read_symlink) (grub_fshelp_node_t node),
 
45
                       enum grub_fshelp_filetype expecttype)
 
46
{
 
47
  grub_err_t err;
 
48
  enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR;
 
49
  int symlinknest = 0;
 
50
 
 
51
  auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
 
52
                                              grub_fshelp_node_t currroot,
 
53
                                              grub_fshelp_node_t *currfound);
 
54
 
 
55
  grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
 
56
                                         grub_fshelp_node_t currroot,
 
57
                                         grub_fshelp_node_t *currfound)
 
58
    {
 
59
      char fpath[grub_strlen (currpath) + 1];
 
60
      char *name = fpath;
 
61
      char *next;
 
62
      //  unsigned int pos = 0;
 
63
      enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
 
64
      grub_fshelp_node_t currnode = currroot;
 
65
      grub_fshelp_node_t oldnode = currroot;
 
66
 
 
67
      auto int NESTED_FUNC_ATTR iterate (const char *filename,
 
68
                                         enum grub_fshelp_filetype filetype,
 
69
                                         grub_fshelp_node_t node);
 
70
 
 
71
      auto void free_node (grub_fshelp_node_t node);
 
72
 
 
73
      void free_node (grub_fshelp_node_t node)
 
74
        {
 
75
          if (node != rootnode && node != currroot)
 
76
            grub_free (node);
 
77
        }
 
78
 
 
79
      int NESTED_FUNC_ATTR iterate (const char *filename,
 
80
                                    enum grub_fshelp_filetype filetype,
 
81
                                    grub_fshelp_node_t node)
 
82
        {
 
83
          if (filetype == GRUB_FSHELP_UNKNOWN ||
 
84
              (grub_strcmp (name, filename) &&
 
85
               (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
 
86
                grub_strncasecmp (name, filename, GRUB_LONG_MAX))))
 
87
            {
 
88
              grub_free (node);
 
89
              return 0;
 
90
            }
 
91
 
 
92
          /* The node is found, stop iterating over the nodes.  */
 
93
          type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
 
94
          oldnode = currnode;
 
95
          currnode = node;
 
96
 
 
97
          return 1;
 
98
        }
 
99
 
 
100
      grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
 
101
 
 
102
      /* Remove all leading slashes.  */
 
103
      while (*name == '/')
 
104
        name++;
 
105
 
 
106
      if (! *name)
 
107
        {
 
108
          *currfound = currnode;
 
109
          return 0;
 
110
        }
 
111
 
 
112
      for (;;)
 
113
        {
 
114
          int found;
 
115
 
 
116
          /* Extract the actual part from the pathname.  */
 
117
          next = grub_strchr (name, '/');
 
118
          if (next)
 
119
            {
 
120
              /* Remove all leading slashes.  */
 
121
              while (*next == '/')
 
122
                *(next++) = '\0';
 
123
            }
 
124
 
 
125
          /* At this point it is expected that the current node is a
 
126
             directory, check if this is true.  */
 
127
          if (type != GRUB_FSHELP_DIR)
 
128
            {
 
129
              free_node (currnode);
 
130
              return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 
131
            }
 
132
 
 
133
          /* Iterate over the directory.  */
 
134
          found = iterate_dir (currnode, iterate);
 
135
          if (! found)
 
136
            {
 
137
              if (grub_errno)
 
138
                return grub_errno;
 
139
 
 
140
              break;
 
141
            }
 
142
 
 
143
          /* Read in the symlink and follow it.  */
 
144
          if (type == GRUB_FSHELP_SYMLINK)
 
145
            {
 
146
              char *symlink;
 
147
 
 
148
              /* Test if the symlink does not loop.  */
 
149
              if (++symlinknest == 8)
 
150
                {
 
151
                  free_node (currnode);
 
152
                  free_node (oldnode);
 
153
                  return grub_error (GRUB_ERR_SYMLINK_LOOP,
 
154
                                     "too deep nesting of symlinks");
 
155
                }
 
156
 
 
157
              symlink = read_symlink (currnode);
 
158
              free_node (currnode);
 
159
 
 
160
              if (!symlink)
 
161
                {
 
162
                  free_node (oldnode);
 
163
                  return grub_errno;
 
164
                }
 
165
 
 
166
              /* The symlink is an absolute path, go back to the root inode.  */
 
167
              if (symlink[0] == '/')
 
168
                {
 
169
                  free_node (oldnode);
 
170
                  oldnode = rootnode;
 
171
                }
 
172
 
 
173
              /* Lookup the node the symlink points to.  */
 
174
              find_file (symlink, oldnode, &currnode);
 
175
              type = foundtype;
 
176
              grub_free (symlink);
 
177
 
 
178
              if (grub_errno)
 
179
                {
 
180
                  free_node (oldnode);
 
181
                  return grub_errno;
 
182
                }
 
183
            }
 
184
 
 
185
          free_node (oldnode);
 
186
 
 
187
          /* Found the node!  */
 
188
          if (! next || *next == '\0')
 
189
            {
 
190
              *currfound = currnode;
 
191
              foundtype = type;
 
192
              return 0;
 
193
            }
 
194
 
 
195
          name = next;
 
196
        }
 
197
 
 
198
      return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 
199
    }
 
200
 
 
201
  if (!path || path[0] != '/')
 
202
    {
 
203
      grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
 
204
      return grub_errno;
 
205
    }
 
206
 
 
207
  err = find_file (path, rootnode, foundnode);
 
208
  if (err)
 
209
    return err;
 
210
 
 
211
  /* Check if the node that was found was of the expected type.  */
 
212
  if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
 
213
    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
 
214
  else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
 
215
    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 
216
 
 
217
  return 0;
 
218
}
 
219
 
 
220
/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
 
221
   beginning with the block POS.  READ_HOOK should be set before
 
222
   reading a block from the file.  GET_BLOCK is used to translate file
 
223
   blocks to disk blocks.  The file is FILESIZE bytes big and the
 
224
   blocks have a size of LOG2BLOCKSIZE (in log2).  */
 
225
grub_ssize_t
 
226
grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
 
227
                       void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 
228
                                                           unsigned offset,
 
229
                                                           unsigned length),
 
230
                       grub_off_t pos, grub_size_t len, char *buf,
 
231
                       grub_disk_addr_t (*get_block) (grub_fshelp_node_t node,
 
232
                                                      grub_disk_addr_t block),
 
233
                       grub_off_t filesize, int log2blocksize)
 
234
{
 
235
  grub_disk_addr_t i, blockcnt;
 
236
  int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
 
237
 
 
238
  /* Adjust LEN so it we can't read past the end of the file.  */
 
239
  if (pos + len > filesize)
 
240
    len = filesize - pos;
 
241
 
 
242
  blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS);
 
243
 
 
244
  for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++)
 
245
    {
 
246
      grub_disk_addr_t blknr;
 
247
      int blockoff = pos & (blocksize - 1);
 
248
      int blockend = blocksize;
 
249
 
 
250
      int skipfirst = 0;
 
251
 
 
252
      blknr = get_block (node, i);
 
253
      if (grub_errno)
 
254
        return -1;
 
255
 
 
256
      blknr = blknr << log2blocksize;
 
257
 
 
258
      /* Last block.  */
 
259
      if (i == blockcnt - 1)
 
260
        {
 
261
          blockend = (len + pos) & (blocksize - 1);
 
262
 
 
263
          /* The last portion is exactly blocksize.  */
 
264
          if (! blockend)
 
265
            blockend = blocksize;
 
266
        }
 
267
 
 
268
      /* First block.  */
 
269
      if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS)))
 
270
        {
 
271
          skipfirst = blockoff;
 
272
          blockend -= skipfirst;
 
273
        }
 
274
 
 
275
      /* If the block number is 0 this block is not stored on disk but
 
276
         is zero filled instead.  */
 
277
      if (blknr)
 
278
        {
 
279
          disk->read_hook = read_hook;
 
280
 
 
281
          grub_disk_read (disk, blknr, skipfirst,
 
282
                          blockend, buf);
 
283
          disk->read_hook = 0;
 
284
          if (grub_errno)
 
285
            return -1;
 
286
        }
 
287
      else
 
288
        grub_memset (buf, 0, blockend);
 
289
 
 
290
      buf += blocksize - skipfirst;
 
291
    }
 
292
 
 
293
  return len;
 
294
}
 
295
 
 
296
unsigned int
 
297
grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow)
 
298
{
 
299
  int mod;
 
300
 
 
301
  *pow = 0;
 
302
  while (blksize > 1)
 
303
    {
 
304
      mod = blksize - ((blksize >> 1) << 1);
 
305
      blksize >>= 1;
 
306
 
 
307
      /* Check if it really is a power of two.  */
 
308
      if (mod)
 
309
        return grub_error (GRUB_ERR_BAD_NUMBER,
 
310
                           "the blocksize is not a power of two");
 
311
      (*pow)++;
 
312
    }
 
313
 
 
314
  return GRUB_ERR_NONE;
 
315
}