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

« back to all changes in this revision

Viewing changes to fs/fshelp.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mto: (17.3.1 squeeze) (1.9.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060105152040-b72i5pq1a82z22yi
Tags: upstream-1.92
ImportĀ upstreamĀ versionĀ 1.92

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