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

« back to all changes in this revision

Viewing changes to grub-core/fs/ext2.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
/* ext2.c - Second Extended filesystem */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2003,2004,2005,2007,2008,2009  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
/* Magic value used to identify an ext2 filesystem.  */
 
21
#define EXT2_MAGIC              0xEF53
 
22
/* Amount of indirect blocks in an inode.  */
 
23
#define INDIRECT_BLOCKS         12
 
24
/* Maximum length of a pathname.  */
 
25
#define EXT2_PATH_MAX           4096
 
26
/* Maximum nesting of symlinks, used to prevent a loop.  */
 
27
#define EXT2_MAX_SYMLINKCNT     8
 
28
 
 
29
/* The good old revision and the default inode size.  */
 
30
#define EXT2_GOOD_OLD_REVISION          0
 
31
#define EXT2_GOOD_OLD_INODE_SIZE        128
 
32
 
 
33
/* Filetype used in directory entry.  */
 
34
#define FILETYPE_UNKNOWN        0
 
35
#define FILETYPE_REG            1
 
36
#define FILETYPE_DIRECTORY      2
 
37
#define FILETYPE_SYMLINK        7
 
38
 
 
39
/* Filetype information as used in inodes.  */
 
40
#define FILETYPE_INO_MASK       0170000
 
41
#define FILETYPE_INO_REG        0100000
 
42
#define FILETYPE_INO_DIRECTORY  0040000
 
43
#define FILETYPE_INO_SYMLINK    0120000
 
44
 
 
45
#include <grub/err.h>
 
46
#include <grub/file.h>
 
47
#include <grub/mm.h>
 
48
#include <grub/misc.h>
 
49
#include <grub/disk.h>
 
50
#include <grub/dl.h>
 
51
#include <grub/types.h>
 
52
#include <grub/fshelp.h>
 
53
 
 
54
/* Log2 size of ext2 block in 512 blocks.  */
 
55
#define LOG2_EXT2_BLOCK_SIZE(data)                      \
 
56
        (grub_le_to_cpu32 (data->sblock.log2_block_size) + 1)
 
57
 
 
58
/* Log2 size of ext2 block in bytes.  */
 
59
#define LOG2_BLOCK_SIZE(data)                                   \
 
60
        (grub_le_to_cpu32 (data->sblock.log2_block_size) + 10)
 
61
 
 
62
/* The size of an ext2 block in bytes.  */
 
63
#define EXT2_BLOCK_SIZE(data)           (1 << LOG2_BLOCK_SIZE (data))
 
64
 
 
65
/* The revision level.  */
 
66
#define EXT2_REVISION(data)     grub_le_to_cpu32 (data->sblock.revision_level)
 
67
 
 
68
/* The inode size.  */
 
69
#define EXT2_INODE_SIZE(data)   \
 
70
        (EXT2_REVISION (data) == EXT2_GOOD_OLD_REVISION \
 
71
         ? EXT2_GOOD_OLD_INODE_SIZE \
 
72
         : grub_le_to_cpu16 (data->sblock.inode_size))
 
73
 
 
74
/* Superblock filesystem feature flags (RW compatible)
 
75
 * A filesystem with any of these enabled can be read and written by a driver
 
76
 * that does not understand them without causing metadata/data corruption.  */
 
77
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC        0x0001
 
78
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES       0x0002
 
79
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL         0x0004
 
80
#define EXT2_FEATURE_COMPAT_EXT_ATTR            0x0008
 
81
#define EXT2_FEATURE_COMPAT_RESIZE_INODE        0x0010
 
82
#define EXT2_FEATURE_COMPAT_DIR_INDEX           0x0020
 
83
/* Superblock filesystem feature flags (RO compatible)
 
84
 * A filesystem with any of these enabled can be safely read by a driver that
 
85
 * does not understand them, but should not be written to, usually because
 
86
 * additional metadata is required.  */
 
87
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER     0x0001
 
88
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE       0x0002
 
89
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR        0x0004
 
90
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM         0x0010
 
91
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK        0x0020
 
92
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE      0x0040
 
93
/* Superblock filesystem feature flags (back-incompatible)
 
94
 * A filesystem with any of these enabled should not be attempted to be read
 
95
 * by a driver that does not understand them, since they usually indicate
 
96
 * metadata format changes that might confuse the reader.  */
 
97
#define EXT2_FEATURE_INCOMPAT_COMPRESSION       0x0001
 
98
#define EXT2_FEATURE_INCOMPAT_FILETYPE          0x0002
 
99
#define EXT3_FEATURE_INCOMPAT_RECOVER           0x0004 /* Needs recovery */
 
100
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV       0x0008 /* Volume is journal device */
 
101
#define EXT2_FEATURE_INCOMPAT_META_BG           0x0010
 
102
#define EXT4_FEATURE_INCOMPAT_EXTENTS           0x0040 /* Extents used */
 
103
#define EXT4_FEATURE_INCOMPAT_64BIT             0x0080
 
104
#define EXT4_FEATURE_INCOMPAT_FLEX_BG           0x0200
 
105
 
 
106
/* The set of back-incompatible features this driver DOES support. Add (OR)
 
107
 * flags here as the related features are implemented into the driver.  */
 
108
#define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \
 
109
                                       | EXT4_FEATURE_INCOMPAT_EXTENTS  \
 
110
                                       | EXT4_FEATURE_INCOMPAT_FLEX_BG )
 
111
/* List of rationales for the ignored "incompatible" features:
 
112
 * needs_recovery: Not really back-incompatible - was added as such to forbid
 
113
 *                 ext2 drivers from mounting an ext3 volume with a dirty
 
114
 *                 journal because they will ignore the journal, but the next
 
115
 *                 ext3 driver to mount the volume will find the journal and
 
116
 *                 replay it, potentially corrupting the metadata written by
 
117
 *                 the ext2 drivers. Safe to ignore for this RO driver.  */
 
118
#define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER )
 
119
 
 
120
 
 
121
#define EXT3_JOURNAL_MAGIC_NUMBER       0xc03b3998U
 
122
 
 
123
#define EXT3_JOURNAL_DESCRIPTOR_BLOCK   1
 
124
#define EXT3_JOURNAL_COMMIT_BLOCK       2
 
125
#define EXT3_JOURNAL_SUPERBLOCK_V1      3
 
126
#define EXT3_JOURNAL_SUPERBLOCK_V2      4
 
127
#define EXT3_JOURNAL_REVOKE_BLOCK       5
 
128
 
 
129
#define EXT3_JOURNAL_FLAG_ESCAPE        1
 
130
#define EXT3_JOURNAL_FLAG_SAME_UUID     2
 
131
#define EXT3_JOURNAL_FLAG_DELETED       4
 
132
#define EXT3_JOURNAL_FLAG_LAST_TAG      8
 
133
 
 
134
#define EXT4_EXTENTS_FLAG               0x80000
 
135
 
 
136
/* The ext2 superblock.  */
 
137
struct grub_ext2_sblock
 
138
{
 
139
  grub_uint32_t total_inodes;
 
140
  grub_uint32_t total_blocks;
 
141
  grub_uint32_t reserved_blocks;
 
142
  grub_uint32_t free_blocks;
 
143
  grub_uint32_t free_inodes;
 
144
  grub_uint32_t first_data_block;
 
145
  grub_uint32_t log2_block_size;
 
146
  grub_uint32_t log2_fragment_size;
 
147
  grub_uint32_t blocks_per_group;
 
148
  grub_uint32_t fragments_per_group;
 
149
  grub_uint32_t inodes_per_group;
 
150
  grub_uint32_t mtime;
 
151
  grub_uint32_t utime;
 
152
  grub_uint16_t mnt_count;
 
153
  grub_uint16_t max_mnt_count;
 
154
  grub_uint16_t magic;
 
155
  grub_uint16_t fs_state;
 
156
  grub_uint16_t error_handling;
 
157
  grub_uint16_t minor_revision_level;
 
158
  grub_uint32_t lastcheck;
 
159
  grub_uint32_t checkinterval;
 
160
  grub_uint32_t creator_os;
 
161
  grub_uint32_t revision_level;
 
162
  grub_uint16_t uid_reserved;
 
163
  grub_uint16_t gid_reserved;
 
164
  grub_uint32_t first_inode;
 
165
  grub_uint16_t inode_size;
 
166
  grub_uint16_t block_group_number;
 
167
  grub_uint32_t feature_compatibility;
 
168
  grub_uint32_t feature_incompat;
 
169
  grub_uint32_t feature_ro_compat;
 
170
  grub_uint16_t uuid[8];
 
171
  char volume_name[16];
 
172
  char last_mounted_on[64];
 
173
  grub_uint32_t compression_info;
 
174
  grub_uint8_t prealloc_blocks;
 
175
  grub_uint8_t prealloc_dir_blocks;
 
176
  grub_uint16_t reserved_gdt_blocks;
 
177
  grub_uint8_t journal_uuid[16];
 
178
  grub_uint32_t journal_inum;
 
179
  grub_uint32_t journal_dev;
 
180
  grub_uint32_t last_orphan;
 
181
  grub_uint32_t hash_seed[4];
 
182
  grub_uint8_t def_hash_version;
 
183
  grub_uint8_t jnl_backup_type;
 
184
  grub_uint16_t reserved_word_pad;
 
185
  grub_uint32_t default_mount_opts;
 
186
  grub_uint32_t first_meta_bg;
 
187
  grub_uint32_t mkfs_time;
 
188
  grub_uint32_t jnl_blocks[17];
 
189
};
 
190
 
 
191
/* The ext2 blockgroup.  */
 
192
struct grub_ext2_block_group
 
193
{
 
194
  grub_uint32_t block_id;
 
195
  grub_uint32_t inode_id;
 
196
  grub_uint32_t inode_table_id;
 
197
  grub_uint16_t free_blocks;
 
198
  grub_uint16_t free_inodes;
 
199
  grub_uint16_t used_dirs;
 
200
  grub_uint16_t pad;
 
201
  grub_uint32_t reserved[3];
 
202
};
 
203
 
 
204
/* The ext2 inode.  */
 
205
struct grub_ext2_inode
 
206
{
 
207
  grub_uint16_t mode;
 
208
  grub_uint16_t uid;
 
209
  grub_uint32_t size;
 
210
  grub_uint32_t atime;
 
211
  grub_uint32_t ctime;
 
212
  grub_uint32_t mtime;
 
213
  grub_uint32_t dtime;
 
214
  grub_uint16_t gid;
 
215
  grub_uint16_t nlinks;
 
216
  grub_uint32_t blockcnt;  /* Blocks of 512 bytes!! */
 
217
  grub_uint32_t flags;
 
218
  grub_uint32_t osd1;
 
219
  union
 
220
  {
 
221
    struct datablocks
 
222
    {
 
223
      grub_uint32_t dir_blocks[INDIRECT_BLOCKS];
 
224
      grub_uint32_t indir_block;
 
225
      grub_uint32_t double_indir_block;
 
226
      grub_uint32_t triple_indir_block;
 
227
    } blocks;
 
228
    char symlink[60];
 
229
  };
 
230
  grub_uint32_t version;
 
231
  grub_uint32_t acl;
 
232
  grub_uint32_t size_high;
 
233
  grub_uint32_t fragment_addr;
 
234
  grub_uint32_t osd2[3];
 
235
};
 
236
 
 
237
/* The header of an ext2 directory entry.  */
 
238
struct ext2_dirent
 
239
{
 
240
  grub_uint32_t inode;
 
241
  grub_uint16_t direntlen;
 
242
  grub_uint8_t namelen;
 
243
  grub_uint8_t filetype;
 
244
};
 
245
 
 
246
struct grub_ext3_journal_header
 
247
{
 
248
  grub_uint32_t magic;
 
249
  grub_uint32_t block_type;
 
250
  grub_uint32_t sequence;
 
251
};
 
252
 
 
253
struct grub_ext3_journal_revoke_header
 
254
{
 
255
  struct grub_ext3_journal_header header;
 
256
  grub_uint32_t count;
 
257
  grub_uint32_t data[0];
 
258
};
 
259
 
 
260
struct grub_ext3_journal_block_tag
 
261
{
 
262
  grub_uint32_t block;
 
263
  grub_uint32_t flags;
 
264
};
 
265
 
 
266
struct grub_ext3_journal_sblock
 
267
{
 
268
  struct grub_ext3_journal_header header;
 
269
  grub_uint32_t block_size;
 
270
  grub_uint32_t maxlen;
 
271
  grub_uint32_t first;
 
272
  grub_uint32_t sequence;
 
273
  grub_uint32_t start;
 
274
};
 
275
 
 
276
#define EXT4_EXT_MAGIC          0xf30a
 
277
 
 
278
struct grub_ext4_extent_header
 
279
{
 
280
  grub_uint16_t magic;
 
281
  grub_uint16_t entries;
 
282
  grub_uint16_t max;
 
283
  grub_uint16_t depth;
 
284
  grub_uint32_t generation;
 
285
};
 
286
 
 
287
struct grub_ext4_extent
 
288
{
 
289
  grub_uint32_t block;
 
290
  grub_uint16_t len;
 
291
  grub_uint16_t start_hi;
 
292
  grub_uint32_t start;
 
293
};
 
294
 
 
295
struct grub_ext4_extent_idx
 
296
{
 
297
  grub_uint32_t block;
 
298
  grub_uint32_t leaf;
 
299
  grub_uint16_t leaf_hi;
 
300
  grub_uint16_t unused;
 
301
};
 
302
 
 
303
struct grub_fshelp_node
 
304
{
 
305
  struct grub_ext2_data *data;
 
306
  struct grub_ext2_inode inode;
 
307
  int ino;
 
308
  int inode_read;
 
309
};
 
310
 
 
311
/* Information about a "mounted" ext2 filesystem.  */
 
312
struct grub_ext2_data
 
313
{
 
314
  struct grub_ext2_sblock sblock;
 
315
  grub_disk_t disk;
 
316
  struct grub_ext2_inode *inode;
 
317
  struct grub_fshelp_node diropen;
 
318
};
 
319
 
 
320
static grub_dl_t my_mod;
 
321
 
 
322
 
 
323
 
 
324
/* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
 
325
   the mounted filesystem DATA.  */
 
326
inline static grub_err_t
 
327
grub_ext2_blockgroup (struct grub_ext2_data *data, int group,
 
328
                      struct grub_ext2_block_group *blkgrp)
 
329
{
 
330
  return grub_disk_read (data->disk,
 
331
                         ((grub_le_to_cpu32 (data->sblock.first_data_block) + 1)
 
332
                          << LOG2_EXT2_BLOCK_SIZE (data)),
 
333
                         group * sizeof (struct grub_ext2_block_group),
 
334
                         sizeof (struct grub_ext2_block_group), blkgrp);
 
335
}
 
336
 
 
337
static struct grub_ext4_extent_header *
 
338
grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf,
 
339
                     struct grub_ext4_extent_header *ext_block,
 
340
                     grub_uint32_t fileblock)
 
341
{
 
342
  struct grub_ext4_extent_idx *index;
 
343
 
 
344
  while (1)
 
345
    {
 
346
      int i;
 
347
      grub_disk_addr_t block;
 
348
 
 
349
      index = (struct grub_ext4_extent_idx *) (ext_block + 1);
 
350
 
 
351
      if (grub_le_to_cpu16(ext_block->magic) != EXT4_EXT_MAGIC)
 
352
        return 0;
 
353
 
 
354
      if (ext_block->depth == 0)
 
355
        return ext_block;
 
356
 
 
357
      for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++)
 
358
        {
 
359
          if (fileblock < grub_le_to_cpu32(index[i].block))
 
360
            break;
 
361
        }
 
362
 
 
363
      if (--i < 0)
 
364
        return 0;
 
365
 
 
366
      block = grub_le_to_cpu16 (index[i].leaf_hi);
 
367
      block = (block << 32) + grub_le_to_cpu32 (index[i].leaf);
 
368
      if (grub_disk_read (data->disk,
 
369
                          block << LOG2_EXT2_BLOCK_SIZE (data),
 
370
                          0, EXT2_BLOCK_SIZE(data), buf))
 
371
        return 0;
 
372
 
 
373
      ext_block = (struct grub_ext4_extent_header *) buf;
 
374
    }
 
375
}
 
376
 
 
377
static grub_disk_addr_t
 
378
grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
 
379
{
 
380
  struct grub_ext2_data *data = node->data;
 
381
  struct grub_ext2_inode *inode = &node->inode;
 
382
  int blknr = -1;
 
383
  unsigned int blksz = EXT2_BLOCK_SIZE (data);
 
384
  int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
 
385
 
 
386
  if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG)
 
387
    {
 
388
      char buf[EXT2_BLOCK_SIZE(data)];
 
389
      struct grub_ext4_extent_header *leaf;
 
390
      struct grub_ext4_extent *ext;
 
391
      int i;
 
392
 
 
393
      leaf = grub_ext4_find_leaf (data, buf,
 
394
                                  (struct grub_ext4_extent_header *) inode->blocks.dir_blocks,
 
395
                                  fileblock);
 
396
      if (! leaf)
 
397
        {
 
398
          grub_error (GRUB_ERR_BAD_FS, "invalid extent");
 
399
          return -1;
 
400
        }
 
401
 
 
402
      ext = (struct grub_ext4_extent *) (leaf + 1);
 
403
      for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++)
 
404
        {
 
405
          if (fileblock < grub_le_to_cpu32 (ext[i].block))
 
406
            break;
 
407
        }
 
408
 
 
409
      if (--i >= 0)
 
410
        {
 
411
          fileblock -= grub_le_to_cpu32 (ext[i].block);
 
412
          if (fileblock >= grub_le_to_cpu16 (ext[i].len))
 
413
            return 0;
 
414
          else
 
415
            {
 
416
              grub_disk_addr_t start;
 
417
 
 
418
              start = grub_le_to_cpu16 (ext[i].start_hi);
 
419
              start = (start << 32) + grub_le_to_cpu32 (ext[i].start);
 
420
 
 
421
              return fileblock + start;
 
422
            }
 
423
        }
 
424
      else
 
425
        {
 
426
          grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
 
427
          return -1;
 
428
        }
 
429
    }
 
430
  /* Direct blocks.  */
 
431
  if (fileblock < INDIRECT_BLOCKS)
 
432
    blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
 
433
  /* Indirect.  */
 
434
  else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
 
435
    {
 
436
      grub_uint32_t indir[blksz / 4];
 
437
 
 
438
      if (grub_disk_read (data->disk,
 
439
                          ((grub_disk_addr_t)
 
440
                           grub_le_to_cpu32 (inode->blocks.indir_block))
 
441
                          << log2_blksz,
 
442
                          0, blksz, indir))
 
443
        return grub_errno;
 
444
 
 
445
      blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
 
446
    }
 
447
  /* Double indirect.  */
 
448
  else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1))
 
449
    {
 
450
      unsigned int perblock = blksz / 4;
 
451
      unsigned int rblock = fileblock - (INDIRECT_BLOCKS
 
452
                                         + blksz / 4);
 
453
      grub_uint32_t indir[blksz / 4];
 
454
 
 
455
      if (grub_disk_read (data->disk,
 
456
                          ((grub_disk_addr_t)
 
457
                           grub_le_to_cpu32 (inode->blocks.double_indir_block))
 
458
                          << log2_blksz,
 
459
                          0, blksz, indir))
 
460
        return grub_errno;
 
461
 
 
462
      if (grub_disk_read (data->disk,
 
463
                          ((grub_disk_addr_t)
 
464
                           grub_le_to_cpu32 (indir[rblock / perblock]))
 
465
                          << log2_blksz,
 
466
                          0, blksz, indir))
 
467
        return grub_errno;
 
468
 
 
469
 
 
470
      blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
 
471
    }
 
472
  /* triple indirect.  */
 
473
  else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1)
 
474
           + (blksz / 4) * (blksz / 4) * (blksz / 4 + 1))
 
475
    {
 
476
      unsigned int perblock = blksz / 4;
 
477
      unsigned int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4
 
478
                                         * (blksz / 4 + 1));
 
479
      grub_uint32_t indir[blksz / 4];
 
480
 
 
481
      if (grub_disk_read (data->disk,
 
482
                          ((grub_disk_addr_t)
 
483
                           grub_le_to_cpu32 (inode->blocks.triple_indir_block))
 
484
                          << log2_blksz,
 
485
                          0, blksz, indir))
 
486
        return grub_errno;
 
487
 
 
488
      if (grub_disk_read (data->disk,
 
489
                          ((grub_disk_addr_t)
 
490
                           grub_le_to_cpu32 (indir[(rblock / perblock) / perblock]))
 
491
                          << log2_blksz,
 
492
                          0, blksz, indir))
 
493
        return grub_errno;
 
494
 
 
495
      if (grub_disk_read (data->disk,
 
496
                          ((grub_disk_addr_t)
 
497
                           grub_le_to_cpu32 (indir[(rblock / perblock) % perblock]))
 
498
                          << log2_blksz,
 
499
                          0, blksz, indir))
 
500
        return grub_errno;
 
501
 
 
502
      blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
 
503
    }
 
504
  else
 
505
    {
 
506
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
507
                  "ext2fs doesn't support quadruple indirect blocks");
 
508
    }
 
509
 
 
510
  return blknr;
 
511
}
 
512
 
 
513
/* Read LEN bytes from the file described by DATA starting with byte
 
514
   POS.  Return the amount of read bytes in READ.  */
 
515
static grub_ssize_t
 
516
grub_ext2_read_file (grub_fshelp_node_t node,
 
517
                     void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 
518
                                        unsigned offset, unsigned length),
 
519
                     grub_off_t pos, grub_size_t len, char *buf)
 
520
{
 
521
  return grub_fshelp_read_file (node->data->disk, node, read_hook,
 
522
                                pos, len, buf, grub_ext2_read_block,
 
523
                                grub_cpu_to_le32 (node->inode.size)
 
524
                                | (((grub_off_t) grub_cpu_to_le32 (node->inode.size_high)) << 32),
 
525
                                LOG2_EXT2_BLOCK_SIZE (node->data));
 
526
 
 
527
}
 
528
 
 
529
 
 
530
/* Read the inode INO for the file described by DATA into INODE.  */
 
531
static grub_err_t
 
532
grub_ext2_read_inode (struct grub_ext2_data *data,
 
533
                      int ino, struct grub_ext2_inode *inode)
 
534
{
 
535
  struct grub_ext2_block_group blkgrp;
 
536
  struct grub_ext2_sblock *sblock = &data->sblock;
 
537
  int inodes_per_block;
 
538
  unsigned int blkno;
 
539
  unsigned int blkoff;
 
540
 
 
541
  /* It is easier to calculate if the first inode is 0.  */
 
542
  ino--;
 
543
 
 
544
  grub_ext2_blockgroup (data,
 
545
                        ino / grub_le_to_cpu32 (sblock->inodes_per_group),
 
546
                        &blkgrp);
 
547
  if (grub_errno)
 
548
    return grub_errno;
 
549
 
 
550
  inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
 
551
  blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
 
552
    / inodes_per_block;
 
553
  blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
 
554
    % inodes_per_block;
 
555
 
 
556
  /* Read the inode.  */
 
557
  if (grub_disk_read (data->disk,
 
558
                      ((grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno)
 
559
                        << LOG2_EXT2_BLOCK_SIZE (data)),
 
560
                      EXT2_INODE_SIZE (data) * blkoff,
 
561
                      sizeof (struct grub_ext2_inode), inode))
 
562
    return grub_errno;
 
563
 
 
564
  return 0;
 
565
}
 
566
 
 
567
static struct grub_ext2_data *
 
568
grub_ext2_mount (grub_disk_t disk)
 
569
{
 
570
  struct grub_ext2_data *data;
 
571
 
 
572
  data = grub_malloc (sizeof (struct grub_ext2_data));
 
573
  if (!data)
 
574
    return 0;
 
575
 
 
576
  /* Read the superblock.  */
 
577
  grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
 
578
                  &data->sblock);
 
579
  if (grub_errno)
 
580
    goto fail;
 
581
 
 
582
  /* Make sure this is an ext2 filesystem.  */
 
583
  if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC)
 
584
    {
 
585
      grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
 
586
      goto fail;
 
587
    }
 
588
 
 
589
  /* Check the FS doesn't have feature bits enabled that we don't support. */
 
590
  if (grub_le_to_cpu32 (data->sblock.feature_incompat)
 
591
        & ~(EXT2_DRIVER_SUPPORTED_INCOMPAT | EXT2_DRIVER_IGNORED_INCOMPAT))
 
592
    {
 
593
      grub_error (GRUB_ERR_BAD_FS, "filesystem has unsupported incompatible features");
 
594
      goto fail;
 
595
    }
 
596
 
 
597
 
 
598
  data->disk = disk;
 
599
 
 
600
  data->diropen.data = data;
 
601
  data->diropen.ino = 2;
 
602
  data->diropen.inode_read = 1;
 
603
 
 
604
  data->inode = &data->diropen.inode;
 
605
 
 
606
  grub_ext2_read_inode (data, 2, data->inode);
 
607
  if (grub_errno)
 
608
    goto fail;
 
609
 
 
610
  return data;
 
611
 
 
612
 fail:
 
613
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
614
    grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
 
615
 
 
616
  grub_free (data);
 
617
  return 0;
 
618
}
 
619
 
 
620
static char *
 
621
grub_ext2_read_symlink (grub_fshelp_node_t node)
 
622
{
 
623
  char *symlink;
 
624
  struct grub_fshelp_node *diro = node;
 
625
 
 
626
  if (! diro->inode_read)
 
627
    {
 
628
      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
 
629
      if (grub_errno)
 
630
        return 0;
 
631
    }
 
632
 
 
633
  symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
 
634
  if (! symlink)
 
635
    return 0;
 
636
 
 
637
  /* If the filesize of the symlink is bigger than
 
638
     60 the symlink is stored in a separate block,
 
639
     otherwise it is stored in the inode.  */
 
640
  if (grub_le_to_cpu32 (diro->inode.size) <= 60)
 
641
    grub_strncpy (symlink,
 
642
                  diro->inode.symlink,
 
643
                  grub_le_to_cpu32 (diro->inode.size));
 
644
  else
 
645
    {
 
646
      grub_ext2_read_file (diro, 0, 0,
 
647
                           grub_le_to_cpu32 (diro->inode.size),
 
648
                           symlink);
 
649
      if (grub_errno)
 
650
        {
 
651
          grub_free (symlink);
 
652
          return 0;
 
653
        }
 
654
    }
 
655
 
 
656
  symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
 
657
  return symlink;
 
658
}
 
659
 
 
660
static int
 
661
grub_ext2_iterate_dir (grub_fshelp_node_t dir,
 
662
                       int NESTED_FUNC_ATTR
 
663
                       (*hook) (const char *filename,
 
664
                                enum grub_fshelp_filetype filetype,
 
665
                                grub_fshelp_node_t node))
 
666
{
 
667
  unsigned int fpos = 0;
 
668
  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
 
669
 
 
670
  if (! diro->inode_read)
 
671
    {
 
672
      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
 
673
      if (grub_errno)
 
674
        return 0;
 
675
    }
 
676
 
 
677
  /* Search the file.  */
 
678
  while (fpos < grub_le_to_cpu32 (diro->inode.size))
 
679
    {
 
680
      struct ext2_dirent dirent;
 
681
 
 
682
      grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent),
 
683
                           (char *) &dirent);
 
684
      if (grub_errno)
 
685
        return 0;
 
686
 
 
687
      if (dirent.direntlen == 0)
 
688
        return 0;
 
689
 
 
690
      if (dirent.namelen != 0)
 
691
        {
 
692
          char filename[dirent.namelen + 1];
 
693
          struct grub_fshelp_node *fdiro;
 
694
          enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
 
695
 
 
696
          grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent),
 
697
                               dirent.namelen, filename);
 
698
          if (grub_errno)
 
699
            return 0;
 
700
 
 
701
          fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
 
702
          if (! fdiro)
 
703
            return 0;
 
704
 
 
705
          fdiro->data = diro->data;
 
706
          fdiro->ino = grub_le_to_cpu32 (dirent.inode);
 
707
 
 
708
          filename[dirent.namelen] = '\0';
 
709
 
 
710
          if (dirent.filetype != FILETYPE_UNKNOWN)
 
711
            {
 
712
              fdiro->inode_read = 0;
 
713
 
 
714
              if (dirent.filetype == FILETYPE_DIRECTORY)
 
715
                type = GRUB_FSHELP_DIR;
 
716
              else if (dirent.filetype == FILETYPE_SYMLINK)
 
717
                type = GRUB_FSHELP_SYMLINK;
 
718
              else if (dirent.filetype == FILETYPE_REG)
 
719
                type = GRUB_FSHELP_REG;
 
720
            }
 
721
          else
 
722
            {
 
723
              /* The filetype can not be read from the dirent, read
 
724
                 the inode to get more information.  */
 
725
              grub_ext2_read_inode (diro->data,
 
726
                                    grub_le_to_cpu32 (dirent.inode),
 
727
                                    &fdiro->inode);
 
728
              if (grub_errno)
 
729
                {
 
730
                  grub_free (fdiro);
 
731
                  return 0;
 
732
                }
 
733
 
 
734
              fdiro->inode_read = 1;
 
735
 
 
736
              if ((grub_le_to_cpu16 (fdiro->inode.mode)
 
737
                   & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
 
738
                type = GRUB_FSHELP_DIR;
 
739
              else if ((grub_le_to_cpu16 (fdiro->inode.mode)
 
740
                        & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
 
741
                type = GRUB_FSHELP_SYMLINK;
 
742
              else if ((grub_le_to_cpu16 (fdiro->inode.mode)
 
743
                        & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
 
744
                type = GRUB_FSHELP_REG;
 
745
            }
 
746
 
 
747
          if (hook (filename, type, fdiro))
 
748
            return 1;
 
749
        }
 
750
 
 
751
      fpos += grub_le_to_cpu16 (dirent.direntlen);
 
752
    }
 
753
 
 
754
  return 0;
 
755
}
 
756
 
 
757
/* Open a file named NAME and initialize FILE.  */
 
758
static grub_err_t
 
759
grub_ext2_open (struct grub_file *file, const char *name)
 
760
{
 
761
  struct grub_ext2_data *data;
 
762
  struct grub_fshelp_node *fdiro = 0;
 
763
  grub_err_t err;
 
764
 
 
765
  grub_dl_ref (my_mod);
 
766
 
 
767
  data = grub_ext2_mount (file->device->disk);
 
768
  if (! data)
 
769
    {
 
770
      err = grub_errno;
 
771
      goto fail;
 
772
    }
 
773
 
 
774
  err = grub_fshelp_find_file (name, &data->diropen, &fdiro,
 
775
                               grub_ext2_iterate_dir,
 
776
                               grub_ext2_read_symlink, GRUB_FSHELP_REG);
 
777
  if (err)
 
778
    goto fail;
 
779
 
 
780
  if (! fdiro->inode_read)
 
781
    {
 
782
      err = grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
 
783
      if (err)
 
784
        goto fail;
 
785
    }
 
786
 
 
787
  grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
 
788
  grub_free (fdiro);
 
789
 
 
790
  file->size = grub_le_to_cpu32 (data->inode->size);
 
791
  file->size |= ((grub_off_t) grub_le_to_cpu32 (data->inode->size_high)) << 32;
 
792
  file->data = data;
 
793
  file->offset = 0;
 
794
 
 
795
  return 0;
 
796
 
 
797
 fail:
 
798
  if (fdiro != &data->diropen)
 
799
    grub_free (fdiro);
 
800
  grub_free (data);
 
801
 
 
802
  grub_dl_unref (my_mod);
 
803
 
 
804
  return err;
 
805
}
 
806
 
 
807
static grub_err_t
 
808
grub_ext2_close (grub_file_t file)
 
809
{
 
810
  grub_free (file->data);
 
811
 
 
812
  grub_dl_unref (my_mod);
 
813
 
 
814
  return GRUB_ERR_NONE;
 
815
}
 
816
 
 
817
/* Read LEN bytes data from FILE into BUF.  */
 
818
static grub_ssize_t
 
819
grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
 
820
{
 
821
  struct grub_ext2_data *data = (struct grub_ext2_data *) file->data;
 
822
 
 
823
  return grub_ext2_read_file (&data->diropen, file->read_hook,
 
824
                              file->offset, len, buf);
 
825
}
 
826
 
 
827
 
 
828
static grub_err_t
 
829
grub_ext2_dir (grub_device_t device, const char *path,
 
830
               int (*hook) (const char *filename,
 
831
                            const struct grub_dirhook_info *info))
 
832
{
 
833
  struct grub_ext2_data *data = 0;
 
834
  struct grub_fshelp_node *fdiro = 0;
 
835
 
 
836
  auto int NESTED_FUNC_ATTR iterate (const char *filename,
 
837
                                     enum grub_fshelp_filetype filetype,
 
838
                                     grub_fshelp_node_t node);
 
839
 
 
840
  int NESTED_FUNC_ATTR iterate (const char *filename,
 
841
                                enum grub_fshelp_filetype filetype,
 
842
                                grub_fshelp_node_t node)
 
843
    {
 
844
      struct grub_dirhook_info info;
 
845
      grub_memset (&info, 0, sizeof (info));
 
846
      if (! node->inode_read)
 
847
        {
 
848
          grub_ext2_read_inode (data, node->ino, &node->inode);
 
849
          if (!grub_errno)
 
850
            node->inode_read = 1;
 
851
          grub_errno = GRUB_ERR_NONE;
 
852
        }
 
853
      if (node->inode_read)
 
854
        {
 
855
          info.mtimeset = 1;
 
856
          info.mtime = grub_le_to_cpu32 (node->inode.mtime);
 
857
        }
 
858
 
 
859
      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
 
860
      grub_free (node);
 
861
      return hook (filename, &info);
 
862
    }
 
863
 
 
864
  grub_dl_ref (my_mod);
 
865
 
 
866
  data = grub_ext2_mount (device->disk);
 
867
  if (! data)
 
868
    goto fail;
 
869
 
 
870
  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
 
871
                         grub_ext2_read_symlink, GRUB_FSHELP_DIR);
 
872
  if (grub_errno)
 
873
    goto fail;
 
874
 
 
875
  grub_ext2_iterate_dir (fdiro, iterate);
 
876
 
 
877
 fail:
 
878
  if (fdiro != &data->diropen)
 
879
    grub_free (fdiro);
 
880
  grub_free (data);
 
881
 
 
882
  grub_dl_unref (my_mod);
 
883
 
 
884
  return grub_errno;
 
885
}
 
886
 
 
887
static grub_err_t
 
888
grub_ext2_label (grub_device_t device, char **label)
 
889
{
 
890
  struct grub_ext2_data *data;
 
891
  grub_disk_t disk = device->disk;
 
892
 
 
893
  grub_dl_ref (my_mod);
 
894
 
 
895
  data = grub_ext2_mount (disk);
 
896
  if (data)
 
897
    *label = grub_strndup (data->sblock.volume_name, 14);
 
898
  else
 
899
    *label = NULL;
 
900
 
 
901
  grub_dl_unref (my_mod);
 
902
 
 
903
  grub_free (data);
 
904
 
 
905
  return grub_errno;
 
906
}
 
907
 
 
908
static grub_err_t
 
909
grub_ext2_uuid (grub_device_t device, char **uuid)
 
910
{
 
911
  struct grub_ext2_data *data;
 
912
  grub_disk_t disk = device->disk;
 
913
 
 
914
  grub_dl_ref (my_mod);
 
915
 
 
916
  data = grub_ext2_mount (disk);
 
917
  if (data)
 
918
    {
 
919
      *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
 
920
                             grub_be_to_cpu16 (data->sblock.uuid[0]),
 
921
                             grub_be_to_cpu16 (data->sblock.uuid[1]),
 
922
                             grub_be_to_cpu16 (data->sblock.uuid[2]),
 
923
                             grub_be_to_cpu16 (data->sblock.uuid[3]),
 
924
                             grub_be_to_cpu16 (data->sblock.uuid[4]),
 
925
                             grub_be_to_cpu16 (data->sblock.uuid[5]),
 
926
                             grub_be_to_cpu16 (data->sblock.uuid[6]),
 
927
                             grub_be_to_cpu16 (data->sblock.uuid[7]));
 
928
    }
 
929
  else
 
930
    *uuid = NULL;
 
931
 
 
932
  grub_dl_unref (my_mod);
 
933
 
 
934
  grub_free (data);
 
935
 
 
936
  return grub_errno;
 
937
}
 
938
 
 
939
/* Get mtime.  */
 
940
static grub_err_t
 
941
grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
 
942
{
 
943
  struct grub_ext2_data *data;
 
944
  grub_disk_t disk = device->disk;
 
945
 
 
946
  grub_dl_ref (my_mod);
 
947
 
 
948
  data = grub_ext2_mount (disk);
 
949
  if (!data)
 
950
    *tm = 0;
 
951
  else
 
952
    *tm = grub_le_to_cpu32 (data->sblock.utime);
 
953
 
 
954
  grub_dl_unref (my_mod);
 
955
 
 
956
  grub_free (data);
 
957
 
 
958
  return grub_errno;
 
959
 
 
960
}
 
961
 
 
962
 
 
963
 
 
964
static struct grub_fs grub_ext2_fs =
 
965
  {
 
966
    .name = "ext2",
 
967
    .dir = grub_ext2_dir,
 
968
    .open = grub_ext2_open,
 
969
    .read = grub_ext2_read,
 
970
    .close = grub_ext2_close,
 
971
    .label = grub_ext2_label,
 
972
    .uuid = grub_ext2_uuid,
 
973
    .mtime = grub_ext2_mtime,
 
974
#ifdef GRUB_UTIL
 
975
    .reserved_first_sector = 1,
 
976
#endif
 
977
    .next = 0
 
978
  };
 
979
 
 
980
GRUB_MOD_INIT(ext2)
 
981
{
 
982
  grub_fs_register (&grub_ext2_fs);
 
983
  my_mod = mod;
 
984
}
 
985
 
 
986
GRUB_MOD_FINI(ext2)
 
987
{
 
988
  grub_fs_unregister (&grub_ext2_fs);
 
989
}