]> git.sur5r.net Git - u-boot/blob - fs/ext4/ext4_common.c
2ddbb50e89086e5b9d6dc0977956e96ab338e2a1
[u-boot] / fs / ext4 / ext4_common.c
1 /*
2  * (C) Copyright 2011 - 2012 Samsung Electronics
3  * EXT4 filesystem implementation in Uboot by
4  * Uma Shankar <uma.shankar@samsung.com>
5  * Manjunatha C Achar <a.manjunatha@samsung.com>
6  *
7  * ext4ls and ext4load : Based on ext2 ls load support in Uboot.
8  *
9  * (C) Copyright 2004
10  * esd gmbh <www.esd-electronics.com>
11  * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
12  *
13  * based on code from grub2 fs/ext2.c and fs/fshelp.c by
14  * GRUB  --  GRand Unified Bootloader
15  * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 2 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30  */
31
32 #include <common.h>
33 #include <ext_common.h>
34 #include <ext4fs.h>
35 #include <malloc.h>
36 #include <stddef.h>
37 #include <linux/stat.h>
38 #include <linux/time.h>
39 #include <asm/byteorder.h>
40 #include "ext4_common.h"
41
42 struct ext2_data *ext4fs_root;
43 struct ext2fs_node *ext4fs_file;
44 uint32_t *ext4fs_indir1_block;
45 int ext4fs_indir1_size;
46 int ext4fs_indir1_blkno = -1;
47 uint32_t *ext4fs_indir2_block;
48 int ext4fs_indir2_size;
49 int ext4fs_indir2_blkno = -1;
50
51 uint32_t *ext4fs_indir3_block;
52 int ext4fs_indir3_size;
53 int ext4fs_indir3_blkno = -1;
54 struct ext2_inode *g_parent_inode;
55 static int symlinknest;
56
57 static struct ext4_extent_header *ext4fs_get_extent_block
58         (struct ext2_data *data, char *buf,
59                 struct ext4_extent_header *ext_block,
60                 uint32_t fileblock, int log2_blksz)
61 {
62         struct ext4_extent_idx *index;
63         unsigned long long block;
64         struct ext_filesystem *fs = get_fs();
65         int i;
66
67         while (1) {
68                 index = (struct ext4_extent_idx *)(ext_block + 1);
69
70                 if (le32_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
71                         return 0;
72
73                 if (ext_block->eh_depth == 0)
74                         return ext_block;
75                 i = -1;
76                 do {
77                         i++;
78                         if (i >= le32_to_cpu(ext_block->eh_entries))
79                                 break;
80                 } while (fileblock > le32_to_cpu(index[i].ei_block));
81
82                 if (--i < 0)
83                         return 0;
84
85                 block = le32_to_cpu(index[i].ei_leaf_hi);
86                 block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo);
87
88                 if (ext4fs_devread(block << log2_blksz, 0, fs->blksz, buf))
89                         ext_block = (struct ext4_extent_header *)buf;
90                 else
91                         return 0;
92         }
93 }
94
95 static int ext4fs_blockgroup
96         (struct ext2_data *data, int group, struct ext2_block_group *blkgrp)
97 {
98         long int blkno;
99         unsigned int blkoff, desc_per_blk;
100
101         desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group);
102
103         blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 +
104                         group / desc_per_blk;
105         blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group);
106
107         debug("ext4fs read %d group descriptor (blkno %ld blkoff %u)\n",
108               group, blkno, blkoff);
109
110         return ext4fs_devread(blkno << LOG2_EXT2_BLOCK_SIZE(data),
111                               blkoff, sizeof(struct ext2_block_group),
112                               (char *)blkgrp);
113 }
114
115 int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
116 {
117         struct ext2_block_group blkgrp;
118         struct ext2_sblock *sblock = &data->sblock;
119         struct ext_filesystem *fs = get_fs();
120         int inodes_per_block, status;
121         long int blkno;
122         unsigned int blkoff;
123
124         /* It is easier to calculate if the first inode is 0. */
125         ino--;
126         status = ext4fs_blockgroup(data, ino / __le32_to_cpu
127                                    (sblock->inodes_per_group), &blkgrp);
128         if (status == 0)
129                 return 0;
130
131         inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz;
132         blkno = __le32_to_cpu(blkgrp.inode_table_id) +
133             (ino % __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
134         blkoff = (ino % inodes_per_block) * fs->inodesz;
135         /* Read the inode. */
136         status = ext4fs_devread(blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff,
137                                 sizeof(struct ext2_inode), (char *)inode);
138         if (status == 0)
139                 return 0;
140
141         return 1;
142 }
143
144 long int read_allocated_block(struct ext2_inode *inode, int fileblock)
145 {
146         long int blknr;
147         int blksz;
148         int log2_blksz;
149         int status;
150         long int rblock;
151         long int perblock_parent;
152         long int perblock_child;
153         unsigned long long start;
154         /* get the blocksize of the filesystem */
155         blksz = EXT2_BLOCK_SIZE(ext4fs_root);
156         log2_blksz = LOG2_EXT2_BLOCK_SIZE(ext4fs_root);
157         if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) {
158                 char *buf = zalloc(blksz);
159                 if (!buf)
160                         return -ENOMEM;
161                 struct ext4_extent_header *ext_block;
162                 struct ext4_extent *extent;
163                 int i = -1;
164                 ext_block = ext4fs_get_extent_block(ext4fs_root, buf,
165                                                     (struct ext4_extent_header
166                                                      *)inode->b.
167                                                     blocks.dir_blocks,
168                                                     fileblock, log2_blksz);
169                 if (!ext_block) {
170                         printf("invalid extent block\n");
171                         free(buf);
172                         return -EINVAL;
173                 }
174
175                 extent = (struct ext4_extent *)(ext_block + 1);
176
177                 do {
178                         i++;
179                         if (i >= le32_to_cpu(ext_block->eh_entries))
180                                 break;
181                 } while (fileblock >= le32_to_cpu(extent[i].ee_block));
182                 if (--i >= 0) {
183                         fileblock -= le32_to_cpu(extent[i].ee_block);
184                         if (fileblock >= le32_to_cpu(extent[i].ee_len)) {
185                                 free(buf);
186                                 return 0;
187                         }
188
189                         start = le32_to_cpu(extent[i].ee_start_hi);
190                         start = (start << 32) +
191                                         le32_to_cpu(extent[i].ee_start_lo);
192                         free(buf);
193                         return fileblock + start;
194                 }
195
196                 printf("Extent Error\n");
197                 free(buf);
198                 return -1;
199         }
200
201         /* Direct blocks. */
202         if (fileblock < INDIRECT_BLOCKS)
203                 blknr = __le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]);
204
205         /* Indirect. */
206         else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) {
207                 if (ext4fs_indir1_block == NULL) {
208                         ext4fs_indir1_block = zalloc(blksz);
209                         if (ext4fs_indir1_block == NULL) {
210                                 printf("** SI ext2fs read block (indir 1)"
211                                         "malloc failed. **\n");
212                                 return -1;
213                         }
214                         ext4fs_indir1_size = blksz;
215                         ext4fs_indir1_blkno = -1;
216                 }
217                 if (blksz != ext4fs_indir1_size) {
218                         free(ext4fs_indir1_block);
219                         ext4fs_indir1_block = NULL;
220                         ext4fs_indir1_size = 0;
221                         ext4fs_indir1_blkno = -1;
222                         ext4fs_indir1_block = zalloc(blksz);
223                         if (ext4fs_indir1_block == NULL) {
224                                 printf("** SI ext2fs read block (indir 1):"
225                                         "malloc failed. **\n");
226                                 return -1;
227                         }
228                         ext4fs_indir1_size = blksz;
229                 }
230                 if ((__le32_to_cpu(inode->b.blocks.indir_block) <<
231                      log2_blksz) != ext4fs_indir1_blkno) {
232                         status =
233                             ext4fs_devread(__le32_to_cpu
234                                            (inode->b.blocks.
235                                             indir_block) << log2_blksz, 0,
236                                            blksz, (char *)ext4fs_indir1_block);
237                         if (status == 0) {
238                                 printf("** SI ext2fs read block (indir 1)"
239                                         "failed. **\n");
240                                 return 0;
241                         }
242                         ext4fs_indir1_blkno =
243                                 __le32_to_cpu(inode->b.blocks.
244                                                indir_block) << log2_blksz;
245                 }
246                 blknr = __le32_to_cpu(ext4fs_indir1_block
247                                       [fileblock - INDIRECT_BLOCKS]);
248         }
249         /* Double indirect. */
250         else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 *
251                                         (blksz / 4 + 1)))) {
252
253                 long int perblock = blksz / 4;
254                 long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4);
255
256                 if (ext4fs_indir1_block == NULL) {
257                         ext4fs_indir1_block = zalloc(blksz);
258                         if (ext4fs_indir1_block == NULL) {
259                                 printf("** DI ext2fs read block (indir 2 1)"
260                                         "malloc failed. **\n");
261                                 return -1;
262                         }
263                         ext4fs_indir1_size = blksz;
264                         ext4fs_indir1_blkno = -1;
265                 }
266                 if (blksz != ext4fs_indir1_size) {
267                         free(ext4fs_indir1_block);
268                         ext4fs_indir1_block = NULL;
269                         ext4fs_indir1_size = 0;
270                         ext4fs_indir1_blkno = -1;
271                         ext4fs_indir1_block = zalloc(blksz);
272                         if (ext4fs_indir1_block == NULL) {
273                                 printf("** DI ext2fs read block (indir 2 1)"
274                                         "malloc failed. **\n");
275                                 return -1;
276                         }
277                         ext4fs_indir1_size = blksz;
278                 }
279                 if ((__le32_to_cpu(inode->b.blocks.double_indir_block) <<
280                      log2_blksz) != ext4fs_indir1_blkno) {
281                         status =
282                             ext4fs_devread(__le32_to_cpu
283                                            (inode->b.blocks.
284                                             double_indir_block) << log2_blksz,
285                                            0, blksz,
286                                            (char *)ext4fs_indir1_block);
287                         if (status == 0) {
288                                 printf("** DI ext2fs read block (indir 2 1)"
289                                         "failed. **\n");
290                                 return -1;
291                         }
292                         ext4fs_indir1_blkno =
293                             __le32_to_cpu(inode->b.blocks.double_indir_block) <<
294                             log2_blksz;
295                 }
296
297                 if (ext4fs_indir2_block == NULL) {
298                         ext4fs_indir2_block = zalloc(blksz);
299                         if (ext4fs_indir2_block == NULL) {
300                                 printf("** DI ext2fs read block (indir 2 2)"
301                                         "malloc failed. **\n");
302                                 return -1;
303                         }
304                         ext4fs_indir2_size = blksz;
305                         ext4fs_indir2_blkno = -1;
306                 }
307                 if (blksz != ext4fs_indir2_size) {
308                         free(ext4fs_indir2_block);
309                         ext4fs_indir2_block = NULL;
310                         ext4fs_indir2_size = 0;
311                         ext4fs_indir2_blkno = -1;
312                         ext4fs_indir2_block = zalloc(blksz);
313                         if (ext4fs_indir2_block == NULL) {
314                                 printf("** DI ext2fs read block (indir 2 2)"
315                                         "malloc failed. **\n");
316                                 return -1;
317                         }
318                         ext4fs_indir2_size = blksz;
319                 }
320                 if ((__le32_to_cpu(ext4fs_indir1_block[rblock / perblock]) <<
321                      log2_blksz) != ext4fs_indir2_blkno) {
322                         status = ext4fs_devread(__le32_to_cpu
323                                                 (ext4fs_indir1_block
324                                                  [rblock /
325                                                   perblock]) << log2_blksz, 0,
326                                                 blksz,
327                                                 (char *)ext4fs_indir2_block);
328                         if (status == 0) {
329                                 printf("** DI ext2fs read block (indir 2 2)"
330                                         "failed. **\n");
331                                 return -1;
332                         }
333                         ext4fs_indir2_blkno =
334                             __le32_to_cpu(ext4fs_indir1_block[rblock
335                                                               /
336                                                               perblock]) <<
337                             log2_blksz;
338                 }
339                 blknr = __le32_to_cpu(ext4fs_indir2_block[rblock % perblock]);
340         }
341         /* Tripple indirect. */
342         else {
343                 rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 +
344                                       (blksz / 4 * blksz / 4));
345                 perblock_child = blksz / 4;
346                 perblock_parent = ((blksz / 4) * (blksz / 4));
347
348                 if (ext4fs_indir1_block == NULL) {
349                         ext4fs_indir1_block = zalloc(blksz);
350                         if (ext4fs_indir1_block == NULL) {
351                                 printf("** TI ext2fs read block (indir 2 1)"
352                                         "malloc failed. **\n");
353                                 return -1;
354                         }
355                         ext4fs_indir1_size = blksz;
356                         ext4fs_indir1_blkno = -1;
357                 }
358                 if (blksz != ext4fs_indir1_size) {
359                         free(ext4fs_indir1_block);
360                         ext4fs_indir1_block = NULL;
361                         ext4fs_indir1_size = 0;
362                         ext4fs_indir1_blkno = -1;
363                         ext4fs_indir1_block = zalloc(blksz);
364                         if (ext4fs_indir1_block == NULL) {
365                                 printf("** TI ext2fs read block (indir 2 1)"
366                                         "malloc failed. **\n");
367                                 return -1;
368                         }
369                         ext4fs_indir1_size = blksz;
370                 }
371                 if ((__le32_to_cpu(inode->b.blocks.triple_indir_block) <<
372                      log2_blksz) != ext4fs_indir1_blkno) {
373                         status = ext4fs_devread
374                             (__le32_to_cpu(inode->b.blocks.triple_indir_block)
375                              << log2_blksz, 0, blksz,
376                              (char *)ext4fs_indir1_block);
377                         if (status == 0) {
378                                 printf("** TI ext2fs read block (indir 2 1)"
379                                         "failed. **\n");
380                                 return -1;
381                         }
382                         ext4fs_indir1_blkno =
383                             __le32_to_cpu(inode->b.blocks.triple_indir_block) <<
384                             log2_blksz;
385                 }
386
387                 if (ext4fs_indir2_block == NULL) {
388                         ext4fs_indir2_block = zalloc(blksz);
389                         if (ext4fs_indir2_block == NULL) {
390                                 printf("** TI ext2fs read block (indir 2 2)"
391                                         "malloc failed. **\n");
392                                 return -1;
393                         }
394                         ext4fs_indir2_size = blksz;
395                         ext4fs_indir2_blkno = -1;
396                 }
397                 if (blksz != ext4fs_indir2_size) {
398                         free(ext4fs_indir2_block);
399                         ext4fs_indir2_block = NULL;
400                         ext4fs_indir2_size = 0;
401                         ext4fs_indir2_blkno = -1;
402                         ext4fs_indir2_block = zalloc(blksz);
403                         if (ext4fs_indir2_block == NULL) {
404                                 printf("** TI ext2fs read block (indir 2 2)"
405                                         "malloc failed. **\n");
406                                 return -1;
407                         }
408                         ext4fs_indir2_size = blksz;
409                 }
410                 if ((__le32_to_cpu(ext4fs_indir1_block[rblock /
411                                                        perblock_parent]) <<
412                      log2_blksz)
413                     != ext4fs_indir2_blkno) {
414                         status = ext4fs_devread(__le32_to_cpu
415                                                 (ext4fs_indir1_block
416                                                  [rblock /
417                                                   perblock_parent]) <<
418                                                 log2_blksz, 0, blksz,
419                                                 (char *)ext4fs_indir2_block);
420                         if (status == 0) {
421                                 printf("** TI ext2fs read block (indir 2 2)"
422                                         "failed. **\n");
423                                 return -1;
424                         }
425                         ext4fs_indir2_blkno =
426                             __le32_to_cpu(ext4fs_indir1_block[rblock /
427                                                               perblock_parent])
428                             << log2_blksz;
429                 }
430
431                 if (ext4fs_indir3_block == NULL) {
432                         ext4fs_indir3_block = zalloc(blksz);
433                         if (ext4fs_indir3_block == NULL) {
434                                 printf("** TI ext2fs read block (indir 2 2)"
435                                         "malloc failed. **\n");
436                                 return -1;
437                         }
438                         ext4fs_indir3_size = blksz;
439                         ext4fs_indir3_blkno = -1;
440                 }
441                 if (blksz != ext4fs_indir3_size) {
442                         free(ext4fs_indir3_block);
443                         ext4fs_indir3_block = NULL;
444                         ext4fs_indir3_size = 0;
445                         ext4fs_indir3_blkno = -1;
446                         ext4fs_indir3_block = zalloc(blksz);
447                         if (ext4fs_indir3_block == NULL) {
448                                 printf("** TI ext2fs read block (indir 2 2)"
449                                         "malloc failed. **\n");
450                                 return -1;
451                         }
452                         ext4fs_indir3_size = blksz;
453                 }
454                 if ((__le32_to_cpu(ext4fs_indir2_block[rblock
455                                                        /
456                                                        perblock_child]) <<
457                      log2_blksz) != ext4fs_indir3_blkno) {
458                         status =
459                             ext4fs_devread(__le32_to_cpu
460                                            (ext4fs_indir2_block
461                                             [(rblock / perblock_child)
462                                              % (blksz / 4)]) << log2_blksz, 0,
463                                            blksz, (char *)ext4fs_indir3_block);
464                         if (status == 0) {
465                                 printf("** TI ext2fs read block (indir 2 2)"
466                                        "failed. **\n");
467                                 return -1;
468                         }
469                         ext4fs_indir3_blkno =
470                             __le32_to_cpu(ext4fs_indir2_block[(rblock /
471                                                                perblock_child) %
472                                                               (blksz /
473                                                                4)]) <<
474                             log2_blksz;
475                 }
476
477                 blknr = __le32_to_cpu(ext4fs_indir3_block
478                                       [rblock % perblock_child]);
479         }
480         debug("ext4fs_read_block %ld\n", blknr);
481
482         return blknr;
483 }
484
485 void ext4fs_close(void)
486 {
487         if ((ext4fs_file != NULL) && (ext4fs_root != NULL)) {
488                 ext4fs_free_node(ext4fs_file, &ext4fs_root->diropen);
489                 ext4fs_file = NULL;
490         }
491         if (ext4fs_root != NULL) {
492                 free(ext4fs_root);
493                 ext4fs_root = NULL;
494         }
495         if (ext4fs_indir1_block != NULL) {
496                 free(ext4fs_indir1_block);
497                 ext4fs_indir1_block = NULL;
498                 ext4fs_indir1_size = 0;
499                 ext4fs_indir1_blkno = -1;
500         }
501         if (ext4fs_indir2_block != NULL) {
502                 free(ext4fs_indir2_block);
503                 ext4fs_indir2_block = NULL;
504                 ext4fs_indir2_size = 0;
505                 ext4fs_indir2_blkno = -1;
506         }
507         if (ext4fs_indir3_block != NULL) {
508                 free(ext4fs_indir3_block);
509                 ext4fs_indir3_block = NULL;
510                 ext4fs_indir3_size = 0;
511                 ext4fs_indir3_blkno = -1;
512         }
513 }
514
515 int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
516                                 struct ext2fs_node **fnode, int *ftype)
517 {
518         unsigned int fpos = 0;
519         int status;
520         struct ext2fs_node *diro = (struct ext2fs_node *) dir;
521
522 #ifdef DEBUG
523         if (name != NULL)
524                 printf("Iterate dir %s\n", name);
525 #endif /* of DEBUG */
526         if (!diro->inode_read) {
527                 status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode);
528                 if (status == 0)
529                         return 0;
530         }
531         /* Search the file.  */
532         while (fpos < __le32_to_cpu(diro->inode.size)) {
533                 struct ext2_dirent dirent;
534
535                 status = ext4fs_read_file(diro, fpos,
536                                            sizeof(struct ext2_dirent),
537                                            (char *) &dirent);
538                 if (status < 1)
539                         return 0;
540
541                 if (dirent.namelen != 0) {
542                         char filename[dirent.namelen + 1];
543                         struct ext2fs_node *fdiro;
544                         int type = FILETYPE_UNKNOWN;
545
546                         status = ext4fs_read_file(diro,
547                                                   fpos +
548                                                   sizeof(struct ext2_dirent),
549                                                   dirent.namelen, filename);
550                         if (status < 1)
551                                 return 0;
552
553                         fdiro = zalloc(sizeof(struct ext2fs_node));
554                         if (!fdiro)
555                                 return 0;
556
557                         fdiro->data = diro->data;
558                         fdiro->ino = __le32_to_cpu(dirent.inode);
559
560                         filename[dirent.namelen] = '\0';
561
562                         if (dirent.filetype != FILETYPE_UNKNOWN) {
563                                 fdiro->inode_read = 0;
564
565                                 if (dirent.filetype == FILETYPE_DIRECTORY)
566                                         type = FILETYPE_DIRECTORY;
567                                 else if (dirent.filetype == FILETYPE_SYMLINK)
568                                         type = FILETYPE_SYMLINK;
569                                 else if (dirent.filetype == FILETYPE_REG)
570                                         type = FILETYPE_REG;
571                         } else {
572                                 status = ext4fs_read_inode(diro->data,
573                                                            __le32_to_cpu
574                                                            (dirent.inode),
575                                                            &fdiro->inode);
576                                 if (status == 0) {
577                                         free(fdiro);
578                                         return 0;
579                                 }
580                                 fdiro->inode_read = 1;
581
582                                 if ((__le16_to_cpu(fdiro->inode.mode) &
583                                      FILETYPE_INO_MASK) ==
584                                     FILETYPE_INO_DIRECTORY) {
585                                         type = FILETYPE_DIRECTORY;
586                                 } else if ((__le16_to_cpu(fdiro->inode.mode)
587                                             & FILETYPE_INO_MASK) ==
588                                            FILETYPE_INO_SYMLINK) {
589                                         type = FILETYPE_SYMLINK;
590                                 } else if ((__le16_to_cpu(fdiro->inode.mode)
591                                             & FILETYPE_INO_MASK) ==
592                                            FILETYPE_INO_REG) {
593                                         type = FILETYPE_REG;
594                                 }
595                         }
596 #ifdef DEBUG
597                         printf("iterate >%s<\n", filename);
598 #endif /* of DEBUG */
599                         if ((name != NULL) && (fnode != NULL)
600                             && (ftype != NULL)) {
601                                 if (strcmp(filename, name) == 0) {
602                                         *ftype = type;
603                                         *fnode = fdiro;
604                                         return 1;
605                                 }
606                         } else {
607                                 if (fdiro->inode_read == 0) {
608                                         status = ext4fs_read_inode(diro->data,
609                                                                  __le32_to_cpu(
610                                                                  dirent.inode),
611                                                                  &fdiro->inode);
612                                         if (status == 0) {
613                                                 free(fdiro);
614                                                 return 0;
615                                         }
616                                         fdiro->inode_read = 1;
617                                 }
618                                 switch (type) {
619                                 case FILETYPE_DIRECTORY:
620                                         printf("<DIR> ");
621                                         break;
622                                 case FILETYPE_SYMLINK:
623                                         printf("<SYM> ");
624                                         break;
625                                 case FILETYPE_REG:
626                                         printf("      ");
627                                         break;
628                                 default:
629                                         printf("< ? > ");
630                                         break;
631                                 }
632                                 printf("%10d %s\n",
633                                         __le32_to_cpu(fdiro->inode.size),
634                                         filename);
635                         }
636                         free(fdiro);
637                 }
638                 fpos += __le16_to_cpu(dirent.direntlen);
639         }
640         return 0;
641 }
642
643 static char *ext4fs_read_symlink(struct ext2fs_node *node)
644 {
645         char *symlink;
646         struct ext2fs_node *diro = node;
647         int status;
648
649         if (!diro->inode_read) {
650                 status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode);
651                 if (status == 0)
652                         return 0;
653         }
654         symlink = zalloc(__le32_to_cpu(diro->inode.size) + 1);
655         if (!symlink)
656                 return 0;
657
658         if (__le32_to_cpu(diro->inode.size) <= 60) {
659                 strncpy(symlink, diro->inode.b.symlink,
660                          __le32_to_cpu(diro->inode.size));
661         } else {
662                 status = ext4fs_read_file(diro, 0,
663                                            __le32_to_cpu(diro->inode.size),
664                                            symlink);
665                 if (status == 0) {
666                         free(symlink);
667                         return 0;
668                 }
669         }
670         symlink[__le32_to_cpu(diro->inode.size)] = '\0';
671         return symlink;
672 }
673
674 static int ext4fs_find_file1(const char *currpath,
675                              struct ext2fs_node *currroot,
676                              struct ext2fs_node **currfound, int *foundtype)
677 {
678         char fpath[strlen(currpath) + 1];
679         char *name = fpath;
680         char *next;
681         int status;
682         int type = FILETYPE_DIRECTORY;
683         struct ext2fs_node *currnode = currroot;
684         struct ext2fs_node *oldnode = currroot;
685
686         strncpy(fpath, currpath, strlen(currpath) + 1);
687
688         /* Remove all leading slashes. */
689         while (*name == '/')
690                 name++;
691
692         if (!*name) {
693                 *currfound = currnode;
694                 return 1;
695         }
696
697         for (;;) {
698                 int found;
699
700                 /* Extract the actual part from the pathname. */
701                 next = strchr(name, '/');
702                 if (next) {
703                         /* Remove all leading slashes. */
704                         while (*next == '/')
705                                 *(next++) = '\0';
706                 }
707
708                 if (type != FILETYPE_DIRECTORY) {
709                         ext4fs_free_node(currnode, currroot);
710                         return 0;
711                 }
712
713                 oldnode = currnode;
714
715                 /* Iterate over the directory. */
716                 found = ext4fs_iterate_dir(currnode, name, &currnode, &type);
717                 if (found == 0)
718                         return 0;
719
720                 if (found == -1)
721                         break;
722
723                 /* Read in the symlink and follow it. */
724                 if (type == FILETYPE_SYMLINK) {
725                         char *symlink;
726
727                         /* Test if the symlink does not loop. */
728                         if (++symlinknest == 8) {
729                                 ext4fs_free_node(currnode, currroot);
730                                 ext4fs_free_node(oldnode, currroot);
731                                 return 0;
732                         }
733
734                         symlink = ext4fs_read_symlink(currnode);
735                         ext4fs_free_node(currnode, currroot);
736
737                         if (!symlink) {
738                                 ext4fs_free_node(oldnode, currroot);
739                                 return 0;
740                         }
741
742                         debug("Got symlink >%s<\n", symlink);
743
744                         if (symlink[0] == '/') {
745                                 ext4fs_free_node(oldnode, currroot);
746                                 oldnode = &ext4fs_root->diropen;
747                         }
748
749                         /* Lookup the node the symlink points to. */
750                         status = ext4fs_find_file1(symlink, oldnode,
751                                                     &currnode, &type);
752
753                         free(symlink);
754
755                         if (status == 0) {
756                                 ext4fs_free_node(oldnode, currroot);
757                                 return 0;
758                         }
759                 }
760
761                 ext4fs_free_node(oldnode, currroot);
762
763                 /* Found the node! */
764                 if (!next || *next == '\0') {
765                         *currfound = currnode;
766                         *foundtype = type;
767                         return 1;
768                 }
769                 name = next;
770         }
771         return -1;
772 }
773
774 int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,
775         struct ext2fs_node **foundnode, int expecttype)
776 {
777         int status;
778         int foundtype = FILETYPE_DIRECTORY;
779
780         symlinknest = 0;
781         if (!path)
782                 return 0;
783
784         status = ext4fs_find_file1(path, rootnode, foundnode, &foundtype);
785         if (status == 0)
786                 return 0;
787
788         /* Check if the node that was found was of the expected type. */
789         if ((expecttype == FILETYPE_REG) && (foundtype != expecttype))
790                 return 0;
791         else if ((expecttype == FILETYPE_DIRECTORY)
792                    && (foundtype != expecttype))
793                 return 0;
794
795         return 1;
796 }
797
798 int ext4fs_open(const char *filename)
799 {
800         struct ext2fs_node *fdiro = NULL;
801         int status;
802         int len;
803
804         if (ext4fs_root == NULL)
805                 return -1;
806
807         ext4fs_file = NULL;
808         status = ext4fs_find_file(filename, &ext4fs_root->diropen, &fdiro,
809                                   FILETYPE_REG);
810         if (status == 0)
811                 goto fail;
812
813         if (!fdiro->inode_read) {
814                 status = ext4fs_read_inode(fdiro->data, fdiro->ino,
815                                 &fdiro->inode);
816                 if (status == 0)
817                         goto fail;
818         }
819         len = __le32_to_cpu(fdiro->inode.size);
820         ext4fs_file = fdiro;
821
822         return len;
823 fail:
824         ext4fs_free_node(fdiro, &ext4fs_root->diropen);
825
826         return -1;
827 }
828
829 int ext4fs_mount(unsigned part_length)
830 {
831         struct ext2_data *data;
832         int status;
833         struct ext_filesystem *fs = get_fs();
834         data = zalloc(sizeof(struct ext2_data));
835         if (!data)
836                 return 0;
837
838         /* Read the superblock. */
839         status = ext4fs_devread(1 * 2, 0, sizeof(struct ext2_sblock),
840                                 (char *)&data->sblock);
841
842         if (status == 0)
843                 goto fail;
844
845         /* Make sure this is an ext2 filesystem. */
846         if (__le16_to_cpu(data->sblock.magic) != EXT2_MAGIC)
847                 goto fail;
848
849         if (__le32_to_cpu(data->sblock.revision_level == 0))
850                 fs->inodesz = 128;
851         else
852                 fs->inodesz = __le16_to_cpu(data->sblock.inode_size);
853
854         debug("EXT2 rev %d, inode_size %d\n",
855                __le32_to_cpu(data->sblock.revision_level), fs->inodesz);
856
857         data->diropen.data = data;
858         data->diropen.ino = 2;
859         data->diropen.inode_read = 1;
860         data->inode = &data->diropen.inode;
861
862         status = ext4fs_read_inode(data, 2, data->inode);
863         if (status == 0)
864                 goto fail;
865
866         ext4fs_root = data;
867
868         return 1;
869 fail:
870         printf("Failed to mount ext2 filesystem...\n");
871         free(data);
872         ext4fs_root = NULL;
873
874         return 0;
875 }