]> git.proxmox.com Git - grub2.git/blob - fs/ext2.c
2008-06-12 Pavel Roskin <proski@gnu.org>
[grub2.git] / fs / ext2.c
1 /* ext2.c - Second Extended filesystem */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,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 /* 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 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
75
76 #define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U
77
78 #define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1
79 #define EXT3_JOURNAL_COMMIT_BLOCK 2
80 #define EXT3_JOURNAL_SUPERBLOCK_V1 3
81 #define EXT3_JOURNAL_SUPERBLOCK_V2 4
82 #define EXT3_JOURNAL_REVOKE_BLOCK 5
83
84 #define EXT3_JOURNAL_FLAG_ESCAPE 1
85 #define EXT3_JOURNAL_FLAG_SAME_UUID 2
86 #define EXT3_JOURNAL_FLAG_DELETED 4
87 #define EXT3_JOURNAL_FLAG_LAST_TAG 8
88
89 /* The ext2 superblock. */
90 struct grub_ext2_sblock
91 {
92 grub_uint32_t total_inodes;
93 grub_uint32_t total_blocks;
94 grub_uint32_t reserved_blocks;
95 grub_uint32_t free_blocks;
96 grub_uint32_t free_inodes;
97 grub_uint32_t first_data_block;
98 grub_uint32_t log2_block_size;
99 grub_uint32_t log2_fragment_size;
100 grub_uint32_t blocks_per_group;
101 grub_uint32_t fragments_per_group;
102 grub_uint32_t inodes_per_group;
103 grub_uint32_t mtime;
104 grub_uint32_t utime;
105 grub_uint16_t mnt_count;
106 grub_uint16_t max_mnt_count;
107 grub_uint16_t magic;
108 grub_uint16_t fs_state;
109 grub_uint16_t error_handling;
110 grub_uint16_t minor_revision_level;
111 grub_uint32_t lastcheck;
112 grub_uint32_t checkinterval;
113 grub_uint32_t creator_os;
114 grub_uint32_t revision_level;
115 grub_uint16_t uid_reserved;
116 grub_uint16_t gid_reserved;
117 grub_uint32_t first_inode;
118 grub_uint16_t inode_size;
119 grub_uint16_t block_group_number;
120 grub_uint32_t feature_compatibility;
121 grub_uint32_t feature_incompat;
122 grub_uint32_t feature_ro_compat;
123 grub_uint16_t uuid[8];
124 char volume_name[16];
125 char last_mounted_on[64];
126 grub_uint32_t compression_info;
127 grub_uint8_t prealloc_blocks;
128 grub_uint8_t prealloc_dir_blocks;
129 grub_uint16_t reserved_gdt_blocks;
130 grub_uint8_t journal_uuid[16];
131 grub_uint32_t journal_inum;
132 grub_uint32_t journal_dev;
133 grub_uint32_t last_orphan;
134 grub_uint32_t hash_seed[4];
135 grub_uint8_t def_hash_version;
136 grub_uint8_t jnl_backup_type;
137 grub_uint16_t reserved_word_pad;
138 grub_uint32_t default_mount_opts;
139 grub_uint32_t first_meta_bg;
140 grub_uint32_t mkfs_time;
141 grub_uint32_t jnl_blocks[17];
142 };
143
144 /* The ext2 blockgroup. */
145 struct grub_ext2_block_group
146 {
147 grub_uint32_t block_id;
148 grub_uint32_t inode_id;
149 grub_uint32_t inode_table_id;
150 grub_uint16_t free_blocks;
151 grub_uint16_t free_inodes;
152 grub_uint16_t used_dirs;
153 grub_uint16_t pad;
154 grub_uint32_t reserved[3];
155 };
156
157 /* The ext2 inode. */
158 struct grub_ext2_inode
159 {
160 grub_uint16_t mode;
161 grub_uint16_t uid;
162 grub_uint32_t size;
163 grub_uint32_t atime;
164 grub_uint32_t ctime;
165 grub_uint32_t mtime;
166 grub_uint32_t dtime;
167 grub_uint16_t gid;
168 grub_uint16_t nlinks;
169 grub_uint32_t blockcnt; /* Blocks of 512 bytes!! */
170 grub_uint32_t flags;
171 grub_uint32_t osd1;
172 union
173 {
174 struct datablocks
175 {
176 grub_uint32_t dir_blocks[INDIRECT_BLOCKS];
177 grub_uint32_t indir_block;
178 grub_uint32_t double_indir_block;
179 grub_uint32_t triple_indir_block;
180 } blocks;
181 char symlink[60];
182 };
183 grub_uint32_t version;
184 grub_uint32_t acl;
185 grub_uint32_t dir_acl;
186 grub_uint32_t fragment_addr;
187 grub_uint32_t osd2[3];
188 };
189
190 /* The header of an ext2 directory entry. */
191 struct ext2_dirent
192 {
193 grub_uint32_t inode;
194 grub_uint16_t direntlen;
195 grub_uint8_t namelen;
196 grub_uint8_t filetype;
197 };
198
199 struct grub_ext3_journal_header
200 {
201 grub_uint32_t magic;
202 grub_uint32_t block_type;
203 grub_uint32_t sequence;
204 };
205
206 struct grub_ext3_journal_revoke_header
207 {
208 struct grub_ext3_journal_header header;
209 grub_uint32_t count;
210 grub_uint32_t data[0];
211 };
212
213 struct grub_ext3_journal_block_tag
214 {
215 grub_uint32_t block;
216 grub_uint32_t flags;
217 };
218
219 struct grub_ext3_journal_sblock
220 {
221 struct grub_ext3_journal_header header;
222 grub_uint32_t block_size;
223 grub_uint32_t maxlen;
224 grub_uint32_t first;
225 grub_uint32_t sequence;
226 grub_uint32_t start;
227 };
228
229 struct grub_fshelp_node
230 {
231 struct grub_ext2_data *data;
232 struct grub_ext2_inode inode;
233 int ino;
234 int inode_read;
235 };
236
237 /* Information about a "mounted" ext2 filesystem. */
238 struct grub_ext2_data
239 {
240 struct grub_ext2_sblock sblock;
241 grub_disk_t disk;
242 struct grub_ext2_inode *inode;
243 struct grub_fshelp_node diropen;
244 struct grub_fshelp_node logfile;
245 grub_fshelp_journal_t journal;
246 };
247
248 #ifndef GRUB_UTIL
249 static grub_dl_t my_mod;
250 #endif
251
252 \f
253
254 /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
255 the mounted filesystem DATA. */
256 inline static grub_err_t
257 grub_ext2_blockgroup (struct grub_ext2_data *data, int group,
258 struct grub_ext2_block_group *blkgrp)
259 {
260 return grub_fshelp_read (data->disk, data->journal,
261 grub_le_to_cpu32 (data->sblock.first_data_block) + 1,
262 group * sizeof (struct grub_ext2_block_group),
263 sizeof (struct grub_ext2_block_group),
264 (char *) blkgrp, LOG2_EXT2_BLOCK_SIZE (data));
265 }
266
267
268 static grub_disk_addr_t
269 grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
270 {
271 struct grub_ext2_data *data = node->data;
272 struct grub_ext2_inode *inode = &node->inode;
273 int blknr;
274 unsigned int blksz = EXT2_BLOCK_SIZE (data);
275 int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
276
277 /* Direct blocks. */
278 if (fileblock < INDIRECT_BLOCKS)
279 blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
280 /* Indirect. */
281 else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
282 {
283 grub_uint32_t indir[blksz / 4];
284
285 if (grub_fshelp_read (data->disk, data->journal,
286 grub_le_to_cpu32 (inode->blocks.indir_block),
287 0, blksz, (char *) indir, log2_blksz))
288 return grub_errno;
289
290 blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
291 }
292 /* Double indirect. */
293 else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1))
294 {
295 unsigned int perblock = blksz / 4;
296 unsigned int rblock = fileblock - (INDIRECT_BLOCKS
297 + blksz / 4);
298 grub_uint32_t indir[blksz / 4];
299
300 if (grub_fshelp_read (data->disk, data->journal,
301 grub_le_to_cpu32 (inode->blocks.double_indir_block),
302 0, blksz, (char *) indir, log2_blksz))
303 return grub_errno;
304
305 if (grub_fshelp_read (data->disk, data->journal,
306 grub_le_to_cpu32 (indir[rblock / perblock]),
307 0, blksz, (char *) indir, log2_blksz))
308 return grub_errno;
309
310
311 blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
312 }
313 /* triple indirect. */
314 else
315 {
316 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
317 "ext2fs doesn't support triple indirect blocks");
318 blknr = -1;
319 }
320
321 return grub_fshelp_map_block (data->journal, blknr);
322 }
323
324 /* Read LEN bytes from the file described by DATA starting with byte
325 POS. Return the amount of read bytes in READ. */
326 static grub_ssize_t
327 grub_ext2_read_file (grub_fshelp_node_t node,
328 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
329 unsigned offset, unsigned length),
330 int pos, grub_size_t len, char *buf)
331 {
332 return grub_fshelp_read_file (node->data->disk, node, read_hook,
333 pos, len, buf, grub_ext2_read_block,
334 node->inode.size,
335 LOG2_EXT2_BLOCK_SIZE (node->data));
336
337 }
338
339
340 /* Read the inode INO for the file described by DATA into INODE. */
341 static grub_err_t
342 grub_ext2_read_inode (struct grub_ext2_data *data,
343 int ino, struct grub_ext2_inode *inode)
344 {
345 struct grub_ext2_block_group blkgrp;
346 struct grub_ext2_sblock *sblock = &data->sblock;
347 int inodes_per_block;
348
349 unsigned int blkno;
350 unsigned int blkoff;
351
352 /* It is easier to calculate if the first inode is 0. */
353 ino--;
354
355 grub_ext2_blockgroup (data,
356 ino / grub_le_to_cpu32 (sblock->inodes_per_group),
357 &blkgrp);
358 if (grub_errno)
359 return grub_errno;
360
361 inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
362 blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
363 / inodes_per_block;
364 blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
365 % inodes_per_block;
366
367 /* Read the inode. */
368 if (grub_fshelp_read (data->disk, data->journal,
369 grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno,
370 EXT2_INODE_SIZE (data) * blkoff,
371 sizeof (struct grub_ext2_inode), (char *) inode,
372 LOG2_EXT2_BLOCK_SIZE (data)))
373 return grub_errno;
374
375 return 0;
376 }
377
378 static void
379 grub_ext3_get_journal (struct grub_ext2_data *data)
380 {
381 char buf[1 << LOG2_BLOCK_SIZE (data)];
382 struct grub_ext3_journal_sblock *jsb;
383 grub_fshelp_journal_t log;
384 int last_num, num, block, log2bs;
385 grub_uint32_t seq;
386
387 auto void next_block (void);
388 void next_block (void)
389 {
390 block++;
391 if (block >= log->last_block)
392 block = log->first_block;
393 }
394
395 data->journal = 0;
396
397 if (! (data->sblock.feature_compatibility & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
398 return;
399
400 if (! data->sblock.journal_inum)
401 return;
402
403 data->logfile.data = data;
404 data->logfile.ino = data->sblock.journal_inum;
405 data->logfile.inode_read = 1;
406
407 if (grub_ext2_read_inode (data, data->logfile.ino, &data->logfile.inode))
408 return;
409
410 log2bs = LOG2_EXT2_BLOCK_SIZE (data);
411 if (grub_fshelp_read_file (data->disk, &data->logfile, 0,
412 0, sizeof (struct grub_ext3_journal_sblock),
413 buf, grub_ext2_read_block,
414 sizeof (buf), log2bs) !=
415 sizeof (struct grub_ext3_journal_sblock))
416 return;
417
418 jsb = (struct grub_ext3_journal_sblock *) &buf[0];
419 if (grub_be_to_cpu32 (jsb->header.magic) != EXT3_JOURNAL_MAGIC_NUMBER)
420 return;
421
422 /* Empty journal. */
423 if (! jsb->start)
424 return;
425
426 log = grub_malloc (sizeof (struct grub_fshelp_journal) +
427 grub_be_to_cpu32 (jsb->maxlen) * sizeof (grub_disk_addr_t));
428 if (! log)
429 return;
430
431 log->type = GRUB_FSHELP_JOURNAL_TYPE_FILE;
432 log->node = &data->logfile;
433 log->get_block = grub_ext2_read_block;
434 log->first_block = grub_be_to_cpu32 (jsb->first);
435 log->last_block = grub_be_to_cpu32 (jsb->maxlen);
436 log->start_block = grub_be_to_cpu32 (jsb->start);
437
438 last_num = num = 0;
439 block = log->start_block;
440 seq = grub_be_to_cpu32 (jsb->sequence);
441
442 while (1)
443 {
444 struct grub_ext3_journal_header *jh;
445
446 if (grub_fshelp_read_file (data->disk, &data->logfile, 0,
447 block << (log2bs + 9), sizeof (buf),
448 buf, grub_ext2_read_block,
449 log->last_block << (log2bs + 9),
450 log2bs) !=
451 (int) sizeof (buf))
452 break;
453
454 jh = (struct grub_ext3_journal_header *) &buf[0];
455 if (grub_be_to_cpu32 (jh->magic) != EXT3_JOURNAL_MAGIC_NUMBER)
456 break;
457
458 if (grub_be_to_cpu32 (jh->sequence) != seq)
459 break;
460
461 log->mapping[num++] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING;
462 next_block();
463
464 switch (grub_be_to_cpu32 (jh->block_type))
465 {
466 case EXT3_JOURNAL_DESCRIPTOR_BLOCK:
467 {
468 struct grub_ext3_journal_block_tag *tag;
469 int ofs, flags;
470
471 ofs = sizeof (struct grub_ext3_journal_header);
472
473 do
474 {
475 tag = (struct grub_ext3_journal_block_tag *) &buf[ofs];
476 ofs += sizeof (struct grub_ext3_journal_block_tag);
477
478 if (ofs > (int) sizeof (buf))
479 break;
480
481 flags = grub_be_to_cpu32 (tag->flags);
482 if (! (flags & EXT3_JOURNAL_FLAG_SAME_UUID))
483 ofs += 16;
484
485 log->mapping[num++] = grub_be_to_cpu32 (tag->block);
486 next_block();
487 }
488 while (! (flags & EXT3_JOURNAL_FLAG_LAST_TAG));
489
490 continue;
491 }
492
493 case EXT3_JOURNAL_COMMIT_BLOCK:
494 {
495 seq++;
496 last_num = num - 1;
497 continue;
498 }
499
500 case EXT3_JOURNAL_REVOKE_BLOCK:
501 {
502 struct grub_ext3_journal_revoke_header *jrh;
503 grub_uint32_t i;
504
505 jrh = (struct grub_ext3_journal_revoke_header *) jh;
506
507 for (i = 0; i < grub_be_to_cpu32 (jrh->count); i++)
508 {
509 int j;
510 grub_uint32_t map;
511
512 map = grub_be_to_cpu32 (jrh->data[i]);
513 for (j = 0; j < num; j++)
514 if (log->mapping[j] == map)
515 log->mapping[j] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING;
516 }
517
518 continue;
519 }
520 default:
521 last_num = 0;
522 goto quit;
523 }
524 }
525
526 quit:
527 if (! last_num)
528 grub_free (log);
529 else
530 {
531 int size;
532
533 size = sizeof (struct grub_fshelp_journal) +
534 last_num * sizeof (grub_disk_addr_t);
535
536 log->num_mappings = last_num;
537 data->journal = grub_realloc (log, size);
538 }
539 }
540
541 static struct grub_ext2_data *
542 grub_ext2_mount (grub_disk_t disk)
543 {
544 struct grub_ext2_data *data;
545
546 data = grub_malloc (sizeof (struct grub_ext2_data));
547 if (!data)
548 return 0;
549
550 /* Read the superblock. */
551 grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
552 (char *) &data->sblock);
553 if (grub_errno)
554 goto fail;
555
556 /* Make sure this is an ext2 filesystem. */
557 if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC)
558 goto fail;
559
560 data->disk = disk;
561 grub_ext3_get_journal (data);
562
563 data->diropen.data = data;
564 data->diropen.ino = 2;
565 data->diropen.inode_read = 1;
566
567 data->inode = &data->diropen.inode;
568
569 grub_ext2_read_inode (data, 2, data->inode);
570 if (grub_errno)
571 goto fail;
572
573 return data;
574
575 fail:
576 grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
577 grub_free (data);
578 return 0;
579 }
580
581 static char *
582 grub_ext2_read_symlink (grub_fshelp_node_t node)
583 {
584 char *symlink;
585 struct grub_fshelp_node *diro = node;
586
587 if (! diro->inode_read)
588 {
589 grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
590 if (grub_errno)
591 return 0;
592 }
593
594 symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
595 if (! symlink)
596 return 0;
597
598 /* If the filesize of the symlink is bigger than
599 60 the symlink is stored in a separate block,
600 otherwise it is stored in the inode. */
601 if (grub_le_to_cpu32 (diro->inode.size) <= 60)
602 grub_strncpy (symlink,
603 diro->inode.symlink,
604 grub_le_to_cpu32 (diro->inode.size));
605 else
606 {
607 grub_ext2_read_file (diro, 0, 0,
608 grub_le_to_cpu32 (diro->inode.size),
609 symlink);
610 if (grub_errno)
611 {
612 grub_free (symlink);
613 return 0;
614 }
615 }
616
617 symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
618 return symlink;
619 }
620
621 static int
622 grub_ext2_iterate_dir (grub_fshelp_node_t dir,
623 int NESTED_FUNC_ATTR
624 (*hook) (const char *filename,
625 enum grub_fshelp_filetype filetype,
626 grub_fshelp_node_t node))
627 {
628 unsigned int fpos = 0;
629 struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
630
631 if (! diro->inode_read)
632 {
633 grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
634 if (grub_errno)
635 return 0;
636 }
637
638 /* Search the file. */
639 while (fpos < grub_le_to_cpu32 (diro->inode.size))
640 {
641 struct ext2_dirent dirent;
642
643 grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent),
644 (char *) &dirent);
645 if (grub_errno)
646 return 0;
647
648 if (dirent.namelen != 0)
649 {
650 char filename[dirent.namelen + 1];
651 struct grub_fshelp_node *fdiro;
652 enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
653
654 grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent),
655 dirent.namelen, filename);
656 if (grub_errno)
657 return 0;
658
659 fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
660 if (! fdiro)
661 return 0;
662
663 fdiro->data = diro->data;
664 fdiro->ino = grub_le_to_cpu32 (dirent.inode);
665
666 filename[dirent.namelen] = '\0';
667
668 if (dirent.filetype != FILETYPE_UNKNOWN)
669 {
670 fdiro->inode_read = 0;
671
672 if (dirent.filetype == FILETYPE_DIRECTORY)
673 type = GRUB_FSHELP_DIR;
674 else if (dirent.filetype == FILETYPE_SYMLINK)
675 type = GRUB_FSHELP_SYMLINK;
676 else if (dirent.filetype == FILETYPE_REG)
677 type = GRUB_FSHELP_REG;
678 }
679 else
680 {
681 /* The filetype can not be read from the dirent, read
682 the inode to get more information. */
683 grub_ext2_read_inode (diro->data,
684 grub_le_to_cpu32 (dirent.inode),
685 &fdiro->inode);
686 if (grub_errno)
687 {
688 grub_free (fdiro);
689 return 0;
690 }
691
692 fdiro->inode_read = 1;
693
694 if ((grub_le_to_cpu16 (fdiro->inode.mode)
695 & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
696 type = GRUB_FSHELP_DIR;
697 else if ((grub_le_to_cpu16 (fdiro->inode.mode)
698 & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
699 type = GRUB_FSHELP_SYMLINK;
700 else if ((grub_le_to_cpu16 (fdiro->inode.mode)
701 & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
702 type = GRUB_FSHELP_REG;
703 }
704
705 if (hook (filename, type, fdiro))
706 return 1;
707 }
708
709 fpos += grub_le_to_cpu16 (dirent.direntlen);
710 }
711
712 return 0;
713 }
714
715 /* Open a file named NAME and initialize FILE. */
716 static grub_err_t
717 grub_ext2_open (struct grub_file *file, const char *name)
718 {
719 struct grub_ext2_data *data;
720 struct grub_fshelp_node *fdiro = 0;
721
722 #ifndef GRUB_UTIL
723 grub_dl_ref (my_mod);
724 #endif
725
726 data = grub_ext2_mount (file->device->disk);
727 if (! data)
728 goto fail;
729
730 grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_ext2_iterate_dir,
731 grub_ext2_read_symlink, GRUB_FSHELP_REG);
732 if (grub_errno)
733 goto fail;
734
735 if (! fdiro->inode_read)
736 {
737 grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
738 if (grub_errno)
739 goto fail;
740 }
741
742 grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
743 grub_free (fdiro);
744
745 file->size = grub_le_to_cpu32 (data->inode->size);
746 file->data = data;
747 file->offset = 0;
748
749 return 0;
750
751 fail:
752 if (fdiro != &data->diropen)
753 grub_free (fdiro);
754 grub_free (data);
755
756 #ifndef GRUB_UTIL
757 grub_dl_unref (my_mod);
758 #endif
759
760 return grub_errno;
761 }
762
763 static grub_err_t
764 grub_ext2_close (grub_file_t file)
765 {
766 if (file->data)
767 {
768 grub_free (((struct grub_ext2_data *) file->data)->journal);
769 grub_free (file->data);
770 }
771
772 #ifndef GRUB_UTIL
773 grub_dl_unref (my_mod);
774 #endif
775
776 return GRUB_ERR_NONE;
777 }
778
779 /* Read LEN bytes data from FILE into BUF. */
780 static grub_ssize_t
781 grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
782 {
783 struct grub_ext2_data *data = (struct grub_ext2_data *) file->data;
784
785 return grub_ext2_read_file (&data->diropen, file->read_hook,
786 file->offset, len, buf);
787 }
788
789
790 static grub_err_t
791 grub_ext2_dir (grub_device_t device, const char *path,
792 int (*hook) (const char *filename, int dir))
793 {
794 struct grub_ext2_data *data = 0;;
795 struct grub_fshelp_node *fdiro = 0;
796
797 auto int NESTED_FUNC_ATTR iterate (const char *filename,
798 enum grub_fshelp_filetype filetype,
799 grub_fshelp_node_t node);
800
801 int NESTED_FUNC_ATTR iterate (const char *filename,
802 enum grub_fshelp_filetype filetype,
803 grub_fshelp_node_t node)
804 {
805 grub_free (node);
806
807 if (filetype == GRUB_FSHELP_DIR)
808 return hook (filename, 1);
809 else
810 return hook (filename, 0);
811
812 return 0;
813 }
814
815 #ifndef GRUB_UTIL
816 grub_dl_ref (my_mod);
817 #endif
818
819 data = grub_ext2_mount (device->disk);
820 if (! data)
821 goto fail;
822
823 grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
824 grub_ext2_read_symlink, GRUB_FSHELP_DIR);
825 if (grub_errno)
826 goto fail;
827
828 grub_ext2_iterate_dir (fdiro, iterate);
829
830 fail:
831 if (fdiro != &data->diropen)
832 grub_free (fdiro);
833 grub_free (data);
834
835 #ifndef GRUB_UTIL
836 grub_dl_unref (my_mod);
837 #endif
838
839 return grub_errno;
840 }
841
842 static grub_err_t
843 grub_ext2_label (grub_device_t device, char **label)
844 {
845 struct grub_ext2_data *data;
846 grub_disk_t disk = device->disk;
847
848 #ifndef GRUB_UTIL
849 grub_dl_ref (my_mod);
850 #endif
851
852 data = grub_ext2_mount (disk);
853 if (data)
854 *label = grub_strndup (data->sblock.volume_name, 14);
855 else
856 *label = NULL;
857
858 #ifndef GRUB_UTIL
859 grub_dl_unref (my_mod);
860 #endif
861
862 grub_free (data);
863
864 return grub_errno;
865 }
866
867 static grub_err_t
868 grub_ext2_uuid (grub_device_t device, char **uuid)
869 {
870 struct grub_ext2_data *data;
871 grub_disk_t disk = device->disk;
872
873 #ifndef GRUB_UTIL
874 grub_dl_ref (my_mod);
875 #endif
876
877 data = grub_ext2_mount (disk);
878 if (data)
879 {
880 *uuid = grub_malloc (40 + sizeof ('\0'));
881 grub_sprintf (*uuid, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
882 grub_be_to_cpu16 (data->sblock.uuid[0]), grub_be_to_cpu16 (data->sblock.uuid[1]),
883 grub_be_to_cpu16 (data->sblock.uuid[2]), grub_be_to_cpu16 (data->sblock.uuid[3]),
884 grub_be_to_cpu16 (data->sblock.uuid[4]), grub_be_to_cpu16 (data->sblock.uuid[5]),
885 grub_be_to_cpu16 (data->sblock.uuid[6]), grub_be_to_cpu16 (data->sblock.uuid[7]));
886 }
887 else
888 *uuid = NULL;
889
890 #ifndef GRUB_UTIL
891 grub_dl_unref (my_mod);
892 #endif
893
894 grub_free (data);
895
896 return grub_errno;
897 }
898
899 \f
900 static struct grub_fs grub_ext2_fs =
901 {
902 .name = "ext2",
903 .dir = grub_ext2_dir,
904 .open = grub_ext2_open,
905 .read = grub_ext2_read,
906 .close = grub_ext2_close,
907 .label = grub_ext2_label,
908 .uuid = grub_ext2_uuid,
909 .next = 0
910 };
911
912 GRUB_MOD_INIT(ext2)
913 {
914 grub_fs_register (&grub_ext2_fs);
915 #ifndef GRUB_UTIL
916 my_mod = mod;
917 #endif
918 }
919
920 GRUB_MOD_FINI(ext2)
921 {
922 grub_fs_unregister (&grub_ext2_fs);
923 }