blob: f02c215ccc2e5676dd1c8fd6783b4d582ae32b69 [file] [log] [blame]
Uma Shankara1596432012-05-25 21:21:44 +05301/*
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 and load support in Uboot.
8 * Ext4 read optimization taken from Open-Moko
9 * Qi bootloader
10 *
11 * (C) Copyright 2004
12 * esd gmbh <www.esd-electronics.com>
13 * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
14 *
15 * based on code from grub2 fs/ext2.c and fs/fshelp.c by
16 * GRUB -- GRand Unified Bootloader
17 * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
18 *
Uma Shankared34f342012-05-25 21:22:49 +053019 * ext4write : Based on generic ext4 protocol.
20 *
Uma Shankara1596432012-05-25 21:21:44 +053021 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 */
35
36#include <common.h>
37#include <malloc.h>
38#include <ext_common.h>
39#include <ext4fs.h>
40#include <linux/stat.h>
41#include <linux/time.h>
42#include <asm/byteorder.h>
Łukasz Majewski32fc16d2012-12-05 08:06:37 +000043#include <div64.h>
Uma Shankara1596432012-05-25 21:21:44 +053044#include "ext4_common.h"
45
46int ext4fs_symlinknest;
Rob Herring94501062012-08-23 11:31:45 +000047struct ext_filesystem ext_fs;
Uma Shankara1596432012-05-25 21:21:44 +053048
49struct ext_filesystem *get_fs(void)
50{
Rob Herring94501062012-08-23 11:31:45 +000051 return &ext_fs;
Uma Shankara1596432012-05-25 21:21:44 +053052}
53
54void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
55{
56 if ((node != &ext4fs_root->diropen) && (node != currroot))
57 free(node);
58}
59
60/*
61 * Taken from openmoko-kernel mailing list: By Andy green
62 * Optimized read file API : collects and defers contiguous sector
63 * reads into one potentially more efficient larger sequential read action
64 */
65int ext4fs_read_file(struct ext2fs_node *node, int pos,
66 unsigned int len, char *buf)
67{
68 int i;
69 int blockcnt;
70 int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data);
71 int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS);
72 unsigned int filesize = __le32_to_cpu(node->inode.size);
73 int previous_block_number = -1;
74 int delayed_start = 0;
75 int delayed_extent = 0;
76 int delayed_skipfirst = 0;
77 int delayed_next = 0;
78 char *delayed_buf = NULL;
79 short status;
80
81 /* Adjust len so it we can't read past the end of the file. */
82 if (len > filesize)
83 len = filesize;
84
85 blockcnt = ((len + pos) + blocksize - 1) / blocksize;
86
87 for (i = pos / blocksize; i < blockcnt; i++) {
88 int blknr;
89 int blockoff = pos % blocksize;
90 int blockend = blocksize;
91 int skipfirst = 0;
92 blknr = read_allocated_block(&(node->inode), i);
93 if (blknr < 0)
94 return -1;
95
96 blknr = blknr << log2blocksize;
97
98 /* Last block. */
99 if (i == blockcnt - 1) {
100 blockend = (len + pos) % blocksize;
101
102 /* The last portion is exactly blocksize. */
103 if (!blockend)
104 blockend = blocksize;
105 }
106
107 /* First block. */
108 if (i == pos / blocksize) {
109 skipfirst = blockoff;
110 blockend -= skipfirst;
111 }
112 if (blknr) {
113 int status;
114
115 if (previous_block_number != -1) {
116 if (delayed_next == blknr) {
117 delayed_extent += blockend;
118 delayed_next += blockend >> SECTOR_BITS;
119 } else { /* spill */
120 status = ext4fs_devread(delayed_start,
121 delayed_skipfirst,
122 delayed_extent,
123 delayed_buf);
124 if (status == 0)
125 return -1;
126 previous_block_number = blknr;
127 delayed_start = blknr;
128 delayed_extent = blockend;
129 delayed_skipfirst = skipfirst;
130 delayed_buf = buf;
131 delayed_next = blknr +
132 (blockend >> SECTOR_BITS);
133 }
134 } else {
135 previous_block_number = blknr;
136 delayed_start = blknr;
137 delayed_extent = blockend;
138 delayed_skipfirst = skipfirst;
139 delayed_buf = buf;
140 delayed_next = blknr +
141 (blockend >> SECTOR_BITS);
142 }
143 } else {
144 if (previous_block_number != -1) {
145 /* spill */
146 status = ext4fs_devread(delayed_start,
147 delayed_skipfirst,
148 delayed_extent,
149 delayed_buf);
150 if (status == 0)
151 return -1;
152 previous_block_number = -1;
153 }
154 memset(buf, 0, blocksize - skipfirst);
155 }
156 buf += blocksize - skipfirst;
157 }
158 if (previous_block_number != -1) {
159 /* spill */
160 status = ext4fs_devread(delayed_start,
161 delayed_skipfirst, delayed_extent,
162 delayed_buf);
163 if (status == 0)
164 return -1;
165 previous_block_number = -1;
166 }
167
168 return len;
169}
170
171int ext4fs_ls(const char *dirname)
172{
173 struct ext2fs_node *dirnode;
174 int status;
175
176 if (dirname == NULL)
177 return 0;
178
179 status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
180 FILETYPE_DIRECTORY);
181 if (status != 1) {
182 printf("** Can not find directory. **\n");
183 return 1;
184 }
185
186 ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
187 ext4fs_free_node(dirnode, &ext4fs_root->diropen);
188
189 return 0;
190}
191
192int ext4fs_read(char *buf, unsigned len)
193{
194 if (ext4fs_root == NULL || ext4fs_file == NULL)
195 return 0;
196
197 return ext4fs_read_file(ext4fs_file, 0, len, buf);
198}
Uma Shankared34f342012-05-25 21:22:49 +0530199
Stephen Warren03e2ecf2012-10-22 06:43:50 +0000200#if defined(CONFIG_EXT4_WRITE)
Uma Shankared34f342012-05-25 21:22:49 +0530201static void ext4fs_update(void)
202{
203 short i;
204 ext4fs_update_journal();
205 struct ext_filesystem *fs = get_fs();
206
207 /* update super block */
208 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
209 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
210
211 /* update block groups */
212 for (i = 0; i < fs->no_blkgrp; i++) {
Simon Glass73c15c62012-10-03 11:37:49 +0000213 fs->bgd[i].bg_checksum = ext4fs_checksum_update(i);
214 put_ext4((uint64_t)(fs->bgd[i].block_id * fs->blksz),
Uma Shankared34f342012-05-25 21:22:49 +0530215 fs->blk_bmaps[i], fs->blksz);
216 }
217
218 /* update inode table groups */
219 for (i = 0; i < fs->no_blkgrp; i++) {
Simon Glass73c15c62012-10-03 11:37:49 +0000220 put_ext4((uint64_t) (fs->bgd[i].inode_id * fs->blksz),
Uma Shankared34f342012-05-25 21:22:49 +0530221 fs->inode_bmaps[i], fs->blksz);
222 }
223
224 /* update the block group descriptor table */
225 put_ext4((uint64_t)(fs->gdtable_blkno * fs->blksz),
226 (struct ext2_block_group *)fs->gdtable,
227 (fs->blksz * fs->no_blk_pergdt));
228
229 ext4fs_dump_metadata();
230
231 gindex = 0;
232 gd_index = 0;
233}
234
235int ext4fs_get_bgdtable(void)
236{
237 int status;
238 int grp_desc_size;
239 struct ext_filesystem *fs = get_fs();
240 grp_desc_size = sizeof(struct ext2_block_group);
241 fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz;
242 if ((fs->no_blkgrp * grp_desc_size) % fs->blksz)
243 fs->no_blk_pergdt++;
244
245 /* allocate memory for gdtable */
246 fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt);
247 if (!fs->gdtable)
248 return -ENOMEM;
249 /* read the group descriptor table */
250 status = ext4fs_devread(fs->gdtable_blkno * fs->sect_perblk, 0,
251 fs->blksz * fs->no_blk_pergdt, fs->gdtable);
252 if (status == 0)
253 goto fail;
254
255 if (ext4fs_log_gdt(fs->gdtable)) {
256 printf("Error in ext4fs_log_gdt\n");
257 return -1;
258 }
259
260 return 0;
261fail:
262 free(fs->gdtable);
263 fs->gdtable = NULL;
264
265 return -1;
266}
267
268static void delete_single_indirect_block(struct ext2_inode *inode)
269{
Simon Glass73c15c62012-10-03 11:37:49 +0000270 struct ext2_block_group *bgd = NULL;
Uma Shankared34f342012-05-25 21:22:49 +0530271 static int prev_bg_bmap_idx = -1;
272 long int blknr;
273 int remainder;
274 int bg_idx;
275 int status;
276 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
277 struct ext_filesystem *fs = get_fs();
278 char *journal_buffer = zalloc(fs->blksz);
279 if (!journal_buffer) {
280 printf("No memory\n");
281 return;
282 }
283 /* get block group descriptor table */
Simon Glass73c15c62012-10-03 11:37:49 +0000284 bgd = (struct ext2_block_group *)fs->gdtable;
Uma Shankared34f342012-05-25 21:22:49 +0530285
286 /* deleting the single indirect block associated with inode */
287 if (inode->b.blocks.indir_block != 0) {
288 debug("SIPB releasing %u\n", inode->b.blocks.indir_block);
289 blknr = inode->b.blocks.indir_block;
290 if (fs->blksz != 1024) {
291 bg_idx = blknr / blk_per_grp;
292 } else {
293 bg_idx = blknr / blk_per_grp;
294 remainder = blknr % blk_per_grp;
295 if (!remainder)
296 bg_idx--;
297 }
298 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Simon Glass73c15c62012-10-03 11:37:49 +0000299 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530300 fs->sb->free_blocks++;
301 /* journal backup */
302 if (prev_bg_bmap_idx != bg_idx) {
303 status =
Simon Glass73c15c62012-10-03 11:37:49 +0000304 ext4fs_devread(bgd[bg_idx].block_id *
Uma Shankared34f342012-05-25 21:22:49 +0530305 fs->sect_perblk, 0, fs->blksz,
306 journal_buffer);
307 if (status == 0)
308 goto fail;
309 if (ext4fs_log_journal
Simon Glass73c15c62012-10-03 11:37:49 +0000310 (journal_buffer, bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530311 goto fail;
312 prev_bg_bmap_idx = bg_idx;
313 }
314 }
315fail:
316 free(journal_buffer);
317}
318
319static void delete_double_indirect_block(struct ext2_inode *inode)
320{
321 int i;
322 short status;
323 static int prev_bg_bmap_idx = -1;
324 long int blknr;
325 int remainder;
326 int bg_idx;
327 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
328 unsigned int *di_buffer = NULL;
329 unsigned int *DIB_start_addr = NULL;
Simon Glass73c15c62012-10-03 11:37:49 +0000330 struct ext2_block_group *bgd = NULL;
Uma Shankared34f342012-05-25 21:22:49 +0530331 struct ext_filesystem *fs = get_fs();
332 char *journal_buffer = zalloc(fs->blksz);
333 if (!journal_buffer) {
334 printf("No memory\n");
335 return;
336 }
337 /* get the block group descriptor table */
Simon Glass73c15c62012-10-03 11:37:49 +0000338 bgd = (struct ext2_block_group *)fs->gdtable;
Uma Shankared34f342012-05-25 21:22:49 +0530339
340 if (inode->b.blocks.double_indir_block != 0) {
341 di_buffer = zalloc(fs->blksz);
342 if (!di_buffer) {
343 printf("No memory\n");
344 return;
345 }
346 DIB_start_addr = (unsigned int *)di_buffer;
347 blknr = inode->b.blocks.double_indir_block;
348 status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz,
349 (char *)di_buffer);
350 for (i = 0; i < fs->blksz / sizeof(int); i++) {
351 if (*di_buffer == 0)
352 break;
353
354 debug("DICB releasing %u\n", *di_buffer);
355 if (fs->blksz != 1024) {
356 bg_idx = (*di_buffer) / blk_per_grp;
357 } else {
358 bg_idx = (*di_buffer) / blk_per_grp;
359 remainder = (*di_buffer) % blk_per_grp;
360 if (!remainder)
361 bg_idx--;
362 }
363 ext4fs_reset_block_bmap(*di_buffer,
364 fs->blk_bmaps[bg_idx], bg_idx);
365 di_buffer++;
Simon Glass73c15c62012-10-03 11:37:49 +0000366 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530367 fs->sb->free_blocks++;
368 /* journal backup */
369 if (prev_bg_bmap_idx != bg_idx) {
Simon Glass73c15c62012-10-03 11:37:49 +0000370 status = ext4fs_devread(bgd[bg_idx].block_id
Uma Shankared34f342012-05-25 21:22:49 +0530371 * fs->sect_perblk, 0,
372 fs->blksz,
373 journal_buffer);
374 if (status == 0)
375 goto fail;
376
377 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000378 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530379 goto fail;
380 prev_bg_bmap_idx = bg_idx;
381 }
382 }
383
384 /* removing the parent double indirect block */
385 blknr = inode->b.blocks.double_indir_block;
386 if (fs->blksz != 1024) {
387 bg_idx = blknr / blk_per_grp;
388 } else {
389 bg_idx = blknr / blk_per_grp;
390 remainder = blknr % blk_per_grp;
391 if (!remainder)
392 bg_idx--;
393 }
394 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Simon Glass73c15c62012-10-03 11:37:49 +0000395 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530396 fs->sb->free_blocks++;
397 /* journal backup */
398 if (prev_bg_bmap_idx != bg_idx) {
399 memset(journal_buffer, '\0', fs->blksz);
Simon Glass73c15c62012-10-03 11:37:49 +0000400 status = ext4fs_devread(bgd[bg_idx].block_id *
Uma Shankared34f342012-05-25 21:22:49 +0530401 fs->sect_perblk, 0, fs->blksz,
402 journal_buffer);
403 if (status == 0)
404 goto fail;
405
406 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000407 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530408 goto fail;
409 prev_bg_bmap_idx = bg_idx;
410 }
411 debug("DIPB releasing %ld\n", blknr);
412 }
413fail:
414 free(DIB_start_addr);
415 free(journal_buffer);
416}
417
418static void delete_triple_indirect_block(struct ext2_inode *inode)
419{
420 int i, j;
421 short status;
422 static int prev_bg_bmap_idx = -1;
423 long int blknr;
424 int remainder;
425 int bg_idx;
426 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
427 unsigned int *tigp_buffer = NULL;
428 unsigned int *tib_start_addr = NULL;
429 unsigned int *tip_buffer = NULL;
430 unsigned int *tipb_start_addr = NULL;
Simon Glass73c15c62012-10-03 11:37:49 +0000431 struct ext2_block_group *bgd = NULL;
Uma Shankared34f342012-05-25 21:22:49 +0530432 struct ext_filesystem *fs = get_fs();
433 char *journal_buffer = zalloc(fs->blksz);
434 if (!journal_buffer) {
435 printf("No memory\n");
436 return;
437 }
438 /* get block group descriptor table */
Simon Glass73c15c62012-10-03 11:37:49 +0000439 bgd = (struct ext2_block_group *)fs->gdtable;
Uma Shankared34f342012-05-25 21:22:49 +0530440
441 if (inode->b.blocks.triple_indir_block != 0) {
442 tigp_buffer = zalloc(fs->blksz);
443 if (!tigp_buffer) {
444 printf("No memory\n");
445 return;
446 }
447 tib_start_addr = (unsigned int *)tigp_buffer;
448 blknr = inode->b.blocks.triple_indir_block;
449 status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz,
450 (char *)tigp_buffer);
451 for (i = 0; i < fs->blksz / sizeof(int); i++) {
452 if (*tigp_buffer == 0)
453 break;
454 debug("tigp buffer releasing %u\n", *tigp_buffer);
455
456 tip_buffer = zalloc(fs->blksz);
457 if (!tip_buffer)
458 goto fail;
459 tipb_start_addr = (unsigned int *)tip_buffer;
460 status = ext4fs_devread((*tigp_buffer) *
461 fs->sect_perblk, 0, fs->blksz,
462 (char *)tip_buffer);
463 for (j = 0; j < fs->blksz / sizeof(int); j++) {
464 if (*tip_buffer == 0)
465 break;
466 if (fs->blksz != 1024) {
467 bg_idx = (*tip_buffer) / blk_per_grp;
468 } else {
469 bg_idx = (*tip_buffer) / blk_per_grp;
470
471 remainder = (*tip_buffer) % blk_per_grp;
472 if (!remainder)
473 bg_idx--;
474 }
475
476 ext4fs_reset_block_bmap(*tip_buffer,
477 fs->blk_bmaps[bg_idx],
478 bg_idx);
479
480 tip_buffer++;
Simon Glass73c15c62012-10-03 11:37:49 +0000481 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530482 fs->sb->free_blocks++;
483 /* journal backup */
484 if (prev_bg_bmap_idx != bg_idx) {
485 status =
Simon Glass73c15c62012-10-03 11:37:49 +0000486 ext4fs_devread(
487 bgd[bg_idx].block_id *
488 fs->sect_perblk, 0,
489 fs->blksz,
490 journal_buffer);
Uma Shankared34f342012-05-25 21:22:49 +0530491 if (status == 0)
492 goto fail;
493
494 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000495 bgd[bg_idx].
Uma Shankared34f342012-05-25 21:22:49 +0530496 block_id))
497 goto fail;
498 prev_bg_bmap_idx = bg_idx;
499 }
500 }
501 free(tipb_start_addr);
502 tipb_start_addr = NULL;
503
504 /*
505 * removing the grand parent blocks
506 * which is connected to inode
507 */
508 if (fs->blksz != 1024) {
509 bg_idx = (*tigp_buffer) / blk_per_grp;
510 } else {
511 bg_idx = (*tigp_buffer) / blk_per_grp;
512
513 remainder = (*tigp_buffer) % blk_per_grp;
514 if (!remainder)
515 bg_idx--;
516 }
517 ext4fs_reset_block_bmap(*tigp_buffer,
518 fs->blk_bmaps[bg_idx], bg_idx);
519
520 tigp_buffer++;
Simon Glass73c15c62012-10-03 11:37:49 +0000521 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530522 fs->sb->free_blocks++;
523 /* journal backup */
524 if (prev_bg_bmap_idx != bg_idx) {
525 memset(journal_buffer, '\0', fs->blksz);
526 status =
Simon Glass73c15c62012-10-03 11:37:49 +0000527 ext4fs_devread(bgd[bg_idx].block_id *
Uma Shankared34f342012-05-25 21:22:49 +0530528 fs->sect_perblk, 0,
529 fs->blksz, journal_buffer);
530 if (status == 0)
531 goto fail;
532
533 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000534 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530535 goto fail;
536 prev_bg_bmap_idx = bg_idx;
537 }
538 }
539
540 /* removing the grand parent triple indirect block */
541 blknr = inode->b.blocks.triple_indir_block;
542 if (fs->blksz != 1024) {
543 bg_idx = blknr / blk_per_grp;
544 } else {
545 bg_idx = blknr / blk_per_grp;
546 remainder = blknr % blk_per_grp;
547 if (!remainder)
548 bg_idx--;
549 }
550 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Simon Glass73c15c62012-10-03 11:37:49 +0000551 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530552 fs->sb->free_blocks++;
553 /* journal backup */
554 if (prev_bg_bmap_idx != bg_idx) {
555 memset(journal_buffer, '\0', fs->blksz);
Simon Glass73c15c62012-10-03 11:37:49 +0000556 status = ext4fs_devread(bgd[bg_idx].block_id *
Uma Shankared34f342012-05-25 21:22:49 +0530557 fs->sect_perblk, 0, fs->blksz,
558 journal_buffer);
559 if (status == 0)
560 goto fail;
561
562 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000563 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530564 goto fail;
565 prev_bg_bmap_idx = bg_idx;
566 }
567 debug("tigp buffer itself releasing %ld\n", blknr);
568 }
569fail:
570 free(tib_start_addr);
571 free(tipb_start_addr);
572 free(journal_buffer);
573}
574
575static int ext4fs_delete_file(int inodeno)
576{
577 struct ext2_inode inode;
578 short status;
579 int i;
580 int remainder;
581 long int blknr;
582 int bg_idx;
583 int ibmap_idx;
584 char *read_buffer = NULL;
585 char *start_block_address = NULL;
586 unsigned int no_blocks;
587
588 static int prev_bg_bmap_idx = -1;
589 unsigned int inodes_per_block;
590 long int blkno;
591 unsigned int blkoff;
592 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
593 unsigned int inode_per_grp = ext4fs_root->sblock.inodes_per_group;
594 struct ext2_inode *inode_buffer = NULL;
Simon Glass73c15c62012-10-03 11:37:49 +0000595 struct ext2_block_group *bgd = NULL;
Uma Shankared34f342012-05-25 21:22:49 +0530596 struct ext_filesystem *fs = get_fs();
597 char *journal_buffer = zalloc(fs->blksz);
598 if (!journal_buffer)
599 return -ENOMEM;
600 /* get the block group descriptor table */
Simon Glass73c15c62012-10-03 11:37:49 +0000601 bgd = (struct ext2_block_group *)fs->gdtable;
Uma Shankared34f342012-05-25 21:22:49 +0530602 status = ext4fs_read_inode(ext4fs_root, inodeno, &inode);
603 if (status == 0)
604 goto fail;
605
606 /* read the block no allocated to a file */
607 no_blocks = inode.size / fs->blksz;
608 if (inode.size % fs->blksz)
609 no_blocks++;
610
611 if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
612 struct ext2fs_node *node_inode =
613 zalloc(sizeof(struct ext2fs_node));
614 if (!node_inode)
615 goto fail;
616 node_inode->data = ext4fs_root;
617 node_inode->ino = inodeno;
618 node_inode->inode_read = 0;
619 memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode));
620
621 for (i = 0; i < no_blocks; i++) {
622 blknr = read_allocated_block(&(node_inode->inode), i);
623 if (fs->blksz != 1024) {
624 bg_idx = blknr / blk_per_grp;
625 } else {
626 bg_idx = blknr / blk_per_grp;
627 remainder = blknr % blk_per_grp;
628 if (!remainder)
629 bg_idx--;
630 }
631 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
632 bg_idx);
633 debug("EXT4_EXTENTS Block releasing %ld: %d\n",
634 blknr, bg_idx);
635
Simon Glass73c15c62012-10-03 11:37:49 +0000636 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530637 fs->sb->free_blocks++;
638
639 /* journal backup */
640 if (prev_bg_bmap_idx != bg_idx) {
641 status =
Simon Glass73c15c62012-10-03 11:37:49 +0000642 ext4fs_devread(bgd[bg_idx].block_id *
Uma Shankared34f342012-05-25 21:22:49 +0530643 fs->sect_perblk, 0,
644 fs->blksz, journal_buffer);
645 if (status == 0)
646 goto fail;
647 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000648 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530649 goto fail;
650 prev_bg_bmap_idx = bg_idx;
651 }
652 }
653 if (node_inode) {
654 free(node_inode);
655 node_inode = NULL;
656 }
657 } else {
658
659 delete_single_indirect_block(&inode);
660 delete_double_indirect_block(&inode);
661 delete_triple_indirect_block(&inode);
662
663 /* read the block no allocated to a file */
664 no_blocks = inode.size / fs->blksz;
665 if (inode.size % fs->blksz)
666 no_blocks++;
667 for (i = 0; i < no_blocks; i++) {
668 blknr = read_allocated_block(&inode, i);
669 if (fs->blksz != 1024) {
670 bg_idx = blknr / blk_per_grp;
671 } else {
672 bg_idx = blknr / blk_per_grp;
673 remainder = blknr % blk_per_grp;
674 if (!remainder)
675 bg_idx--;
676 }
677 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
678 bg_idx);
679 debug("ActualB releasing %ld: %d\n", blknr, bg_idx);
680
Simon Glass73c15c62012-10-03 11:37:49 +0000681 bgd[bg_idx].free_blocks++;
Uma Shankared34f342012-05-25 21:22:49 +0530682 fs->sb->free_blocks++;
683 /* journal backup */
684 if (prev_bg_bmap_idx != bg_idx) {
685 memset(journal_buffer, '\0', fs->blksz);
Simon Glass73c15c62012-10-03 11:37:49 +0000686 status = ext4fs_devread(bgd[bg_idx].block_id
Uma Shankared34f342012-05-25 21:22:49 +0530687 * fs->sect_perblk,
688 0, fs->blksz,
689 journal_buffer);
690 if (status == 0)
691 goto fail;
692 if (ext4fs_log_journal(journal_buffer,
Simon Glass73c15c62012-10-03 11:37:49 +0000693 bgd[bg_idx].block_id))
Uma Shankared34f342012-05-25 21:22:49 +0530694 goto fail;
695 prev_bg_bmap_idx = bg_idx;
696 }
697 }
698 }
699
700 /* from the inode no to blockno */
701 inodes_per_block = fs->blksz / fs->inodesz;
702 ibmap_idx = inodeno / inode_per_grp;
703
704 /* get the block no */
705 inodeno--;
Simon Glass73c15c62012-10-03 11:37:49 +0000706 blkno = __le32_to_cpu(bgd[ibmap_idx].inode_table_id) +
Uma Shankared34f342012-05-25 21:22:49 +0530707 (inodeno % __le32_to_cpu(inode_per_grp)) / inodes_per_block;
708
709 /* get the offset of the inode */
710 blkoff = ((inodeno) % inodes_per_block) * fs->inodesz;
711
712 /* read the block no containing the inode */
713 read_buffer = zalloc(fs->blksz);
714 if (!read_buffer)
715 goto fail;
716 start_block_address = read_buffer;
717 status = ext4fs_devread(blkno * fs->sect_perblk,
718 0, fs->blksz, read_buffer);
719 if (status == 0)
720 goto fail;
721
722 if (ext4fs_log_journal(read_buffer, blkno))
723 goto fail;
724
725 read_buffer = read_buffer + blkoff;
726 inode_buffer = (struct ext2_inode *)read_buffer;
727 memset(inode_buffer, '\0', sizeof(struct ext2_inode));
728
729 /* write the inode to original position in inode table */
730 if (ext4fs_put_metadata(start_block_address, blkno))
731 goto fail;
732
733 /* update the respective inode bitmaps */
734 inodeno++;
735 ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx);
Simon Glass73c15c62012-10-03 11:37:49 +0000736 bgd[ibmap_idx].free_inodes++;
Uma Shankared34f342012-05-25 21:22:49 +0530737 fs->sb->free_inodes++;
738 /* journal backup */
739 memset(journal_buffer, '\0', fs->blksz);
Simon Glass73c15c62012-10-03 11:37:49 +0000740 status = ext4fs_devread(bgd[ibmap_idx].inode_id *
Uma Shankared34f342012-05-25 21:22:49 +0530741 fs->sect_perblk, 0, fs->blksz, journal_buffer);
742 if (status == 0)
743 goto fail;
Simon Glass73c15c62012-10-03 11:37:49 +0000744 if (ext4fs_log_journal(journal_buffer, bgd[ibmap_idx].inode_id))
Uma Shankared34f342012-05-25 21:22:49 +0530745 goto fail;
746
747 ext4fs_update();
748 ext4fs_deinit();
749
750 if (ext4fs_init() != 0) {
751 printf("error in File System init\n");
752 goto fail;
753 }
754
755 free(start_block_address);
756 free(journal_buffer);
757
758 return 0;
759fail:
760 free(start_block_address);
761 free(journal_buffer);
762
763 return -1;
764}
765
766int ext4fs_init(void)
767{
768 short status;
769 int i;
770 unsigned int real_free_blocks = 0;
771 struct ext_filesystem *fs = get_fs();
772
773 /* populate fs */
774 fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root);
775 fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root);
776 fs->sect_perblk = fs->blksz / SECTOR_SIZE;
777
778 /* get the superblock */
779 fs->sb = zalloc(SUPERBLOCK_SIZE);
780 if (!fs->sb)
781 return -ENOMEM;
782 if (!ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE,
783 (char *)fs->sb))
784 goto fail;
785
786 /* init journal */
787 if (ext4fs_init_journal())
788 goto fail;
789
790 /* get total no of blockgroups */
791 fs->no_blkgrp = (uint32_t)ext4fs_div_roundup(
792 (ext4fs_root->sblock.total_blocks -
793 ext4fs_root->sblock.first_data_block),
794 ext4fs_root->sblock.blocks_per_group);
795
796 /* get the block group descriptor table */
797 fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1);
798 if (ext4fs_get_bgdtable() == -1) {
799 printf("Error in getting the block group descriptor table\n");
800 goto fail;
801 }
Simon Glass73c15c62012-10-03 11:37:49 +0000802 fs->bgd = (struct ext2_block_group *)fs->gdtable;
Uma Shankared34f342012-05-25 21:22:49 +0530803
804 /* load all the available bitmap block of the partition */
805 fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *));
806 if (!fs->blk_bmaps)
807 goto fail;
808 for (i = 0; i < fs->no_blkgrp; i++) {
809 fs->blk_bmaps[i] = zalloc(fs->blksz);
810 if (!fs->blk_bmaps[i])
811 goto fail;
812 }
813
814 for (i = 0; i < fs->no_blkgrp; i++) {
815 status =
Simon Glass73c15c62012-10-03 11:37:49 +0000816 ext4fs_devread(fs->bgd[i].block_id * fs->sect_perblk, 0,
Uma Shankared34f342012-05-25 21:22:49 +0530817 fs->blksz, (char *)fs->blk_bmaps[i]);
818 if (status == 0)
819 goto fail;
820 }
821
822 /* load all the available inode bitmap of the partition */
823 fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *));
824 if (!fs->inode_bmaps)
825 goto fail;
826 for (i = 0; i < fs->no_blkgrp; i++) {
827 fs->inode_bmaps[i] = zalloc(fs->blksz);
828 if (!fs->inode_bmaps[i])
829 goto fail;
830 }
831
832 for (i = 0; i < fs->no_blkgrp; i++) {
Simon Glass73c15c62012-10-03 11:37:49 +0000833 status = ext4fs_devread(fs->bgd[i].inode_id * fs->sect_perblk,
Uma Shankared34f342012-05-25 21:22:49 +0530834 0, fs->blksz,
835 (char *)fs->inode_bmaps[i]);
836 if (status == 0)
837 goto fail;
838 }
839
840 /*
841 * check filesystem consistency with free blocks of file system
842 * some time we observed that superblock freeblocks does not match
843 * with the blockgroups freeblocks when improper
844 * reboot of a linux kernel
845 */
846 for (i = 0; i < fs->no_blkgrp; i++)
Simon Glass73c15c62012-10-03 11:37:49 +0000847 real_free_blocks = real_free_blocks + fs->bgd[i].free_blocks;
Uma Shankared34f342012-05-25 21:22:49 +0530848 if (real_free_blocks != fs->sb->free_blocks)
849 fs->sb->free_blocks = real_free_blocks;
850
851 return 0;
852fail:
853 ext4fs_deinit();
854
855 return -1;
856}
857
858void ext4fs_deinit(void)
859{
860 int i;
861 struct ext2_inode inode_journal;
862 struct journal_superblock_t *jsb;
863 long int blknr;
864 struct ext_filesystem *fs = get_fs();
865
866 /* free journal */
867 char *temp_buff = zalloc(fs->blksz);
868 if (temp_buff) {
869 ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
870 &inode_journal);
871 blknr = read_allocated_block(&inode_journal,
872 EXT2_JOURNAL_SUPERBLOCK);
873 ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz,
874 temp_buff);
875 jsb = (struct journal_superblock_t *)temp_buff;
876 jsb->s_start = cpu_to_be32(0);
877 put_ext4((uint64_t) (blknr * fs->blksz),
878 (struct journal_superblock_t *)temp_buff, fs->blksz);
879 free(temp_buff);
880 }
881 ext4fs_free_journal();
882
883 /* get the superblock */
884 ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, (char *)fs->sb);
885 fs->sb->feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
886 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
887 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
888 free(fs->sb);
889 fs->sb = NULL;
890
891 if (fs->blk_bmaps) {
892 for (i = 0; i < fs->no_blkgrp; i++) {
893 free(fs->blk_bmaps[i]);
894 fs->blk_bmaps[i] = NULL;
895 }
896 free(fs->blk_bmaps);
897 fs->blk_bmaps = NULL;
898 }
899
900 if (fs->inode_bmaps) {
901 for (i = 0; i < fs->no_blkgrp; i++) {
902 free(fs->inode_bmaps[i]);
903 fs->inode_bmaps[i] = NULL;
904 }
905 free(fs->inode_bmaps);
906 fs->inode_bmaps = NULL;
907 }
908
909
910 free(fs->gdtable);
911 fs->gdtable = NULL;
Simon Glass73c15c62012-10-03 11:37:49 +0000912 fs->bgd = NULL;
Uma Shankared34f342012-05-25 21:22:49 +0530913 /*
914 * reinitiliazed the global inode and
915 * block bitmap first execution check variables
916 */
917 fs->first_pass_ibmap = 0;
918 fs->first_pass_bbmap = 0;
919 fs->curr_inode_no = 0;
920 fs->curr_blkno = 0;
921}
922
923static int ext4fs_write_file(struct ext2_inode *file_inode,
924 int pos, unsigned int len, char *buf)
925{
926 int i;
927 int blockcnt;
928 int log2blocksize = LOG2_EXT2_BLOCK_SIZE(ext4fs_root);
929 unsigned int filesize = __le32_to_cpu(file_inode->size);
930 struct ext_filesystem *fs = get_fs();
931 int previous_block_number = -1;
932 int delayed_start = 0;
933 int delayed_extent = 0;
Uma Shankared34f342012-05-25 21:22:49 +0530934 int delayed_next = 0;
935 char *delayed_buf = NULL;
936
937 /* Adjust len so it we can't read past the end of the file. */
938 if (len > filesize)
939 len = filesize;
940
941 blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz;
942
943 for (i = pos / fs->blksz; i < blockcnt; i++) {
944 long int blknr;
945 int blockend = fs->blksz;
946 int skipfirst = 0;
947 blknr = read_allocated_block(file_inode, i);
948 if (blknr < 0)
949 return -1;
950
951 blknr = blknr << log2blocksize;
952
953 if (blknr) {
954 if (previous_block_number != -1) {
955 if (delayed_next == blknr) {
956 delayed_extent += blockend;
957 delayed_next += blockend >> SECTOR_BITS;
958 } else { /* spill */
959 put_ext4((uint64_t) (delayed_start *
960 SECTOR_SIZE),
961 delayed_buf,
962 (uint32_t) delayed_extent);
963 previous_block_number = blknr;
964 delayed_start = blknr;
965 delayed_extent = blockend;
Uma Shankared34f342012-05-25 21:22:49 +0530966 delayed_buf = buf;
967 delayed_next = blknr +
968 (blockend >> SECTOR_BITS);
969 }
970 } else {
971 previous_block_number = blknr;
972 delayed_start = blknr;
973 delayed_extent = blockend;
Uma Shankared34f342012-05-25 21:22:49 +0530974 delayed_buf = buf;
975 delayed_next = blknr +
976 (blockend >> SECTOR_BITS);
977 }
978 } else {
979 if (previous_block_number != -1) {
980 /* spill */
981 put_ext4((uint64_t) (delayed_start *
982 SECTOR_SIZE), delayed_buf,
983 (uint32_t) delayed_extent);
984 previous_block_number = -1;
985 }
986 memset(buf, 0, fs->blksz - skipfirst);
987 }
988 buf += fs->blksz - skipfirst;
989 }
990 if (previous_block_number != -1) {
991 /* spill */
992 put_ext4((uint64_t) (delayed_start * SECTOR_SIZE),
993 delayed_buf, (uint32_t) delayed_extent);
994 previous_block_number = -1;
995 }
996
997 return len;
998}
999
1000int ext4fs_write(const char *fname, unsigned char *buffer,
1001 unsigned long sizebytes)
1002{
1003 int ret = 0;
1004 struct ext2_inode *file_inode = NULL;
1005 unsigned char *inode_buffer = NULL;
1006 int parent_inodeno;
1007 int inodeno;
1008 time_t timestamp = 0;
1009
1010 uint64_t bytes_reqd_for_file;
1011 unsigned int blks_reqd_for_file;
1012 unsigned int blocks_remaining;
1013 int existing_file_inodeno;
Uma Shankared34f342012-05-25 21:22:49 +05301014 char *temp_ptr = NULL;
1015 long int itable_blkno;
1016 long int parent_itable_blkno;
1017 long int blkoff;
1018 struct ext2_sblock *sblock = &(ext4fs_root->sblock);
1019 unsigned int inodes_per_block;
1020 unsigned int ibmap_idx;
1021 struct ext_filesystem *fs = get_fs();
Łukasz Majewski13d43552012-12-05 08:06:40 +00001022 ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
1023 memset(filename, 0x00, sizeof(filename));
1024
Uma Shankared34f342012-05-25 21:22:49 +05301025 g_parent_inode = zalloc(sizeof(struct ext2_inode));
1026 if (!g_parent_inode)
1027 goto fail;
1028
1029 if (ext4fs_init() != 0) {
1030 printf("error in File System init\n");
1031 return -1;
1032 }
1033 inodes_per_block = fs->blksz / fs->inodesz;
1034 parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE);
1035 if (parent_inodeno == -1)
1036 goto fail;
1037 if (ext4fs_iget(parent_inodeno, g_parent_inode))
1038 goto fail;
1039 /* check if the filename is already present in root */
1040 existing_file_inodeno = ext4fs_filename_check(filename);
1041 if (existing_file_inodeno != -1) {
1042 ret = ext4fs_delete_file(existing_file_inodeno);
1043 fs->first_pass_bbmap = 0;
1044 fs->curr_blkno = 0;
1045
1046 fs->first_pass_ibmap = 0;
1047 fs->curr_inode_no = 0;
1048 if (ret)
1049 goto fail;
1050 }
1051 /* calucalate how many blocks required */
1052 bytes_reqd_for_file = sizebytes;
Łukasz Majewski32fc16d2012-12-05 08:06:37 +00001053 blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz);
1054 if (do_div(bytes_reqd_for_file, fs->blksz) != 0) {
Uma Shankared34f342012-05-25 21:22:49 +05301055 blks_reqd_for_file++;
1056 debug("total bytes for a file %u\n", blks_reqd_for_file);
1057 }
1058 blocks_remaining = blks_reqd_for_file;
1059 /* test for available space in partition */
1060 if (fs->sb->free_blocks < blks_reqd_for_file) {
1061 printf("Not enough space on partition !!!\n");
1062 goto fail;
1063 }
1064
1065 ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG);
1066 /* prepare file inode */
1067 inode_buffer = zalloc(fs->inodesz);
1068 if (!inode_buffer)
1069 goto fail;
1070 file_inode = (struct ext2_inode *)inode_buffer;
1071 file_inode->mode = S_IFREG | S_IRWXU |
1072 S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH;
1073 /* ToDo: Update correct time */
1074 file_inode->mtime = timestamp;
1075 file_inode->atime = timestamp;
1076 file_inode->ctime = timestamp;
1077 file_inode->nlinks = 1;
1078 file_inode->size = sizebytes;
1079
1080 /* Allocate data blocks */
1081 ext4fs_allocate_blocks(file_inode, blocks_remaining,
1082 &blks_reqd_for_file);
1083 file_inode->blockcnt = (blks_reqd_for_file * fs->blksz) / SECTOR_SIZE;
1084
1085 temp_ptr = zalloc(fs->blksz);
1086 if (!temp_ptr)
1087 goto fail;
1088 ibmap_idx = inodeno / ext4fs_root->sblock.inodes_per_group;
1089 inodeno--;
Simon Glass73c15c62012-10-03 11:37:49 +00001090 itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) +
Uma Shankared34f342012-05-25 21:22:49 +05301091 (inodeno % __le32_to_cpu(sblock->inodes_per_group)) /
1092 inodes_per_block;
1093 blkoff = (inodeno % inodes_per_block) * fs->inodesz;
1094 ext4fs_devread(itable_blkno * fs->sect_perblk, 0, fs->blksz, temp_ptr);
1095 if (ext4fs_log_journal(temp_ptr, itable_blkno))
1096 goto fail;
1097
1098 memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz);
1099 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
1100 goto fail;
1101 /* copy the file content into data blocks */
1102 if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) {
1103 printf("Error in copying content\n");
1104 goto fail;
1105 }
1106 ibmap_idx = parent_inodeno / ext4fs_root->sblock.inodes_per_group;
1107 parent_inodeno--;
Simon Glass73c15c62012-10-03 11:37:49 +00001108 parent_itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) +
Uma Shankared34f342012-05-25 21:22:49 +05301109 (parent_inodeno %
1110 __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
1111 blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz;
1112 if (parent_itable_blkno != itable_blkno) {
1113 memset(temp_ptr, '\0', fs->blksz);
1114 ext4fs_devread(parent_itable_blkno * fs->sect_perblk,
1115 0, fs->blksz, temp_ptr);
1116 if (ext4fs_log_journal(temp_ptr, parent_itable_blkno))
1117 goto fail;
1118
1119 memcpy(temp_ptr + blkoff, g_parent_inode,
1120 sizeof(struct ext2_inode));
1121 if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno))
1122 goto fail;
1123 free(temp_ptr);
1124 } else {
1125 /*
1126 * If parent and child fall in same inode table block
1127 * both should be kept in 1 buffer
1128 */
1129 memcpy(temp_ptr + blkoff, g_parent_inode,
1130 sizeof(struct ext2_inode));
1131 gd_index--;
1132 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
1133 goto fail;
1134 free(temp_ptr);
1135 }
1136 ext4fs_update();
1137 ext4fs_deinit();
1138
1139 fs->first_pass_bbmap = 0;
1140 fs->curr_blkno = 0;
1141 fs->first_pass_ibmap = 0;
1142 fs->curr_inode_no = 0;
1143 free(inode_buffer);
1144 free(g_parent_inode);
1145 g_parent_inode = NULL;
1146
1147 return 0;
1148fail:
1149 ext4fs_deinit();
1150 free(inode_buffer);
1151 free(g_parent_inode);
1152 g_parent_inode = NULL;
1153
1154 return -1;
1155}
1156#endif