blob: b674b6faebe2d214e58909d4f90dc2857972b1ab [file] [log] [blame]
Simon Glass293d7fb2012-12-26 09:53:28 +00001/*
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 *
19 * ext4write : Based on generic ext4 protocol.
20 *
Wolfgang Denk1a459662013-07-08 09:37:19 +020021 * SPDX-License-Identifier: GPL-2.0+
Simon Glass293d7fb2012-12-26 09:53:28 +000022 */
23
24
25#include <common.h>
26#include <linux/stat.h>
27#include <div64.h>
28#include "ext4_common.h"
29
30static void ext4fs_update(void)
31{
32 short i;
33 ext4fs_update_journal();
34 struct ext_filesystem *fs = get_fs();
35
36 /* update super block */
37 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
38 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
39
40 /* update block groups */
41 for (i = 0; i < fs->no_blkgrp; i++) {
42 fs->bgd[i].bg_checksum = ext4fs_checksum_update(i);
Ma Haijun05508702014-01-08 08:15:33 +080043 put_ext4((uint64_t)((uint64_t)fs->bgd[i].block_id * (uint64_t)fs->blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +000044 fs->blk_bmaps[i], fs->blksz);
45 }
46
47 /* update inode table groups */
48 for (i = 0; i < fs->no_blkgrp; i++) {
Ma Haijun05508702014-01-08 08:15:33 +080049 put_ext4((uint64_t) ((uint64_t)fs->bgd[i].inode_id * (uint64_t)fs->blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +000050 fs->inode_bmaps[i], fs->blksz);
51 }
52
53 /* update the block group descriptor table */
Ma Haijun05508702014-01-08 08:15:33 +080054 put_ext4((uint64_t)((uint64_t)fs->gdtable_blkno * (uint64_t)fs->blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +000055 (struct ext2_block_group *)fs->gdtable,
56 (fs->blksz * fs->no_blk_pergdt));
57
58 ext4fs_dump_metadata();
59
60 gindex = 0;
61 gd_index = 0;
62}
63
64int ext4fs_get_bgdtable(void)
65{
66 int status;
67 int grp_desc_size;
68 struct ext_filesystem *fs = get_fs();
69 grp_desc_size = sizeof(struct ext2_block_group);
70 fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz;
71 if ((fs->no_blkgrp * grp_desc_size) % fs->blksz)
72 fs->no_blk_pergdt++;
73
74 /* allocate memory for gdtable */
75 fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt);
76 if (!fs->gdtable)
77 return -ENOMEM;
78 /* read the group descriptor table */
Frederic Leroy04735e92013-06-26 18:11:25 +020079 status = ext4fs_devread((lbaint_t)fs->gdtable_blkno * fs->sect_perblk,
80 0, fs->blksz * fs->no_blk_pergdt, fs->gdtable);
Simon Glass293d7fb2012-12-26 09:53:28 +000081 if (status == 0)
82 goto fail;
83
84 if (ext4fs_log_gdt(fs->gdtable)) {
85 printf("Error in ext4fs_log_gdt\n");
86 return -1;
87 }
88
89 return 0;
90fail:
91 free(fs->gdtable);
92 fs->gdtable = NULL;
93
94 return -1;
95}
96
97static void delete_single_indirect_block(struct ext2_inode *inode)
98{
99 struct ext2_block_group *bgd = NULL;
100 static int prev_bg_bmap_idx = -1;
101 long int blknr;
102 int remainder;
103 int bg_idx;
104 int status;
105 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
106 struct ext_filesystem *fs = get_fs();
107 char *journal_buffer = zalloc(fs->blksz);
108 if (!journal_buffer) {
109 printf("No memory\n");
110 return;
111 }
112 /* get block group descriptor table */
113 bgd = (struct ext2_block_group *)fs->gdtable;
114
115 /* deleting the single indirect block associated with inode */
116 if (inode->b.blocks.indir_block != 0) {
117 debug("SIPB releasing %u\n", inode->b.blocks.indir_block);
118 blknr = inode->b.blocks.indir_block;
119 if (fs->blksz != 1024) {
120 bg_idx = blknr / blk_per_grp;
121 } else {
122 bg_idx = blknr / blk_per_grp;
123 remainder = blknr % blk_per_grp;
124 if (!remainder)
125 bg_idx--;
126 }
127 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
128 bgd[bg_idx].free_blocks++;
129 fs->sb->free_blocks++;
130 /* journal backup */
131 if (prev_bg_bmap_idx != bg_idx) {
132 status =
Frederic Leroy04735e92013-06-26 18:11:25 +0200133 ext4fs_devread((lbaint_t)bgd[bg_idx].block_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000134 fs->sect_perblk, 0, fs->blksz,
135 journal_buffer);
136 if (status == 0)
137 goto fail;
138 if (ext4fs_log_journal
139 (journal_buffer, bgd[bg_idx].block_id))
140 goto fail;
141 prev_bg_bmap_idx = bg_idx;
142 }
143 }
144fail:
145 free(journal_buffer);
146}
147
148static void delete_double_indirect_block(struct ext2_inode *inode)
149{
150 int i;
151 short status;
152 static int prev_bg_bmap_idx = -1;
153 long int blknr;
154 int remainder;
155 int bg_idx;
156 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
157 unsigned int *di_buffer = NULL;
158 unsigned int *DIB_start_addr = NULL;
159 struct ext2_block_group *bgd = NULL;
160 struct ext_filesystem *fs = get_fs();
161 char *journal_buffer = zalloc(fs->blksz);
162 if (!journal_buffer) {
163 printf("No memory\n");
164 return;
165 }
166 /* get the block group descriptor table */
167 bgd = (struct ext2_block_group *)fs->gdtable;
168
169 if (inode->b.blocks.double_indir_block != 0) {
170 di_buffer = zalloc(fs->blksz);
171 if (!di_buffer) {
172 printf("No memory\n");
173 return;
174 }
175 DIB_start_addr = (unsigned int *)di_buffer;
176 blknr = inode->b.blocks.double_indir_block;
Frederic Leroy04735e92013-06-26 18:11:25 +0200177 status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
178 fs->blksz, (char *)di_buffer);
Simon Glass293d7fb2012-12-26 09:53:28 +0000179 for (i = 0; i < fs->blksz / sizeof(int); i++) {
180 if (*di_buffer == 0)
181 break;
182
183 debug("DICB releasing %u\n", *di_buffer);
184 if (fs->blksz != 1024) {
185 bg_idx = (*di_buffer) / blk_per_grp;
186 } else {
187 bg_idx = (*di_buffer) / blk_per_grp;
188 remainder = (*di_buffer) % blk_per_grp;
189 if (!remainder)
190 bg_idx--;
191 }
192 ext4fs_reset_block_bmap(*di_buffer,
193 fs->blk_bmaps[bg_idx], bg_idx);
194 di_buffer++;
195 bgd[bg_idx].free_blocks++;
196 fs->sb->free_blocks++;
197 /* journal backup */
198 if (prev_bg_bmap_idx != bg_idx) {
Frederic Leroy04735e92013-06-26 18:11:25 +0200199 status = ext4fs_devread((lbaint_t)
200 bgd[bg_idx].block_id
Simon Glass293d7fb2012-12-26 09:53:28 +0000201 * fs->sect_perblk, 0,
202 fs->blksz,
203 journal_buffer);
204 if (status == 0)
205 goto fail;
206
207 if (ext4fs_log_journal(journal_buffer,
208 bgd[bg_idx].block_id))
209 goto fail;
210 prev_bg_bmap_idx = bg_idx;
211 }
212 }
213
214 /* removing the parent double indirect block */
215 blknr = inode->b.blocks.double_indir_block;
216 if (fs->blksz != 1024) {
217 bg_idx = blknr / blk_per_grp;
218 } else {
219 bg_idx = blknr / blk_per_grp;
220 remainder = blknr % blk_per_grp;
221 if (!remainder)
222 bg_idx--;
223 }
224 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
225 bgd[bg_idx].free_blocks++;
226 fs->sb->free_blocks++;
227 /* journal backup */
228 if (prev_bg_bmap_idx != bg_idx) {
229 memset(journal_buffer, '\0', fs->blksz);
Frederic Leroy04735e92013-06-26 18:11:25 +0200230 status = ext4fs_devread((lbaint_t)bgd[bg_idx].block_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000231 fs->sect_perblk, 0, fs->blksz,
232 journal_buffer);
233 if (status == 0)
234 goto fail;
235
236 if (ext4fs_log_journal(journal_buffer,
237 bgd[bg_idx].block_id))
238 goto fail;
239 prev_bg_bmap_idx = bg_idx;
240 }
241 debug("DIPB releasing %ld\n", blknr);
242 }
243fail:
244 free(DIB_start_addr);
245 free(journal_buffer);
246}
247
248static void delete_triple_indirect_block(struct ext2_inode *inode)
249{
250 int i, j;
251 short status;
252 static int prev_bg_bmap_idx = -1;
253 long int blknr;
254 int remainder;
255 int bg_idx;
256 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
257 unsigned int *tigp_buffer = NULL;
258 unsigned int *tib_start_addr = NULL;
259 unsigned int *tip_buffer = NULL;
260 unsigned int *tipb_start_addr = NULL;
261 struct ext2_block_group *bgd = NULL;
262 struct ext_filesystem *fs = get_fs();
263 char *journal_buffer = zalloc(fs->blksz);
264 if (!journal_buffer) {
265 printf("No memory\n");
266 return;
267 }
268 /* get block group descriptor table */
269 bgd = (struct ext2_block_group *)fs->gdtable;
270
271 if (inode->b.blocks.triple_indir_block != 0) {
272 tigp_buffer = zalloc(fs->blksz);
273 if (!tigp_buffer) {
274 printf("No memory\n");
275 return;
276 }
277 tib_start_addr = (unsigned int *)tigp_buffer;
278 blknr = inode->b.blocks.triple_indir_block;
Frederic Leroy04735e92013-06-26 18:11:25 +0200279 status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
280 fs->blksz, (char *)tigp_buffer);
Simon Glass293d7fb2012-12-26 09:53:28 +0000281 for (i = 0; i < fs->blksz / sizeof(int); i++) {
282 if (*tigp_buffer == 0)
283 break;
284 debug("tigp buffer releasing %u\n", *tigp_buffer);
285
286 tip_buffer = zalloc(fs->blksz);
287 if (!tip_buffer)
288 goto fail;
289 tipb_start_addr = (unsigned int *)tip_buffer;
Frederic Leroy04735e92013-06-26 18:11:25 +0200290 status = ext4fs_devread((lbaint_t)(*tigp_buffer) *
Simon Glass293d7fb2012-12-26 09:53:28 +0000291 fs->sect_perblk, 0, fs->blksz,
292 (char *)tip_buffer);
293 for (j = 0; j < fs->blksz / sizeof(int); j++) {
294 if (*tip_buffer == 0)
295 break;
296 if (fs->blksz != 1024) {
297 bg_idx = (*tip_buffer) / blk_per_grp;
298 } else {
299 bg_idx = (*tip_buffer) / blk_per_grp;
300
301 remainder = (*tip_buffer) % blk_per_grp;
302 if (!remainder)
303 bg_idx--;
304 }
305
306 ext4fs_reset_block_bmap(*tip_buffer,
307 fs->blk_bmaps[bg_idx],
308 bg_idx);
309
310 tip_buffer++;
311 bgd[bg_idx].free_blocks++;
312 fs->sb->free_blocks++;
313 /* journal backup */
314 if (prev_bg_bmap_idx != bg_idx) {
315 status =
316 ext4fs_devread(
Frederic Leroy04735e92013-06-26 18:11:25 +0200317 (lbaint_t)
Simon Glass293d7fb2012-12-26 09:53:28 +0000318 bgd[bg_idx].block_id *
319 fs->sect_perblk, 0,
320 fs->blksz,
321 journal_buffer);
322 if (status == 0)
323 goto fail;
324
325 if (ext4fs_log_journal(journal_buffer,
326 bgd[bg_idx].
327 block_id))
328 goto fail;
329 prev_bg_bmap_idx = bg_idx;
330 }
331 }
332 free(tipb_start_addr);
333 tipb_start_addr = NULL;
334
335 /*
336 * removing the grand parent blocks
337 * which is connected to inode
338 */
339 if (fs->blksz != 1024) {
340 bg_idx = (*tigp_buffer) / blk_per_grp;
341 } else {
342 bg_idx = (*tigp_buffer) / blk_per_grp;
343
344 remainder = (*tigp_buffer) % blk_per_grp;
345 if (!remainder)
346 bg_idx--;
347 }
348 ext4fs_reset_block_bmap(*tigp_buffer,
349 fs->blk_bmaps[bg_idx], bg_idx);
350
351 tigp_buffer++;
352 bgd[bg_idx].free_blocks++;
353 fs->sb->free_blocks++;
354 /* journal backup */
355 if (prev_bg_bmap_idx != bg_idx) {
356 memset(journal_buffer, '\0', fs->blksz);
357 status =
Frederic Leroy04735e92013-06-26 18:11:25 +0200358 ext4fs_devread((lbaint_t)
359 bgd[bg_idx].block_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000360 fs->sect_perblk, 0,
361 fs->blksz, journal_buffer);
362 if (status == 0)
363 goto fail;
364
365 if (ext4fs_log_journal(journal_buffer,
366 bgd[bg_idx].block_id))
367 goto fail;
368 prev_bg_bmap_idx = bg_idx;
369 }
370 }
371
372 /* removing the grand parent triple indirect block */
373 blknr = inode->b.blocks.triple_indir_block;
374 if (fs->blksz != 1024) {
375 bg_idx = blknr / blk_per_grp;
376 } else {
377 bg_idx = blknr / blk_per_grp;
378 remainder = blknr % blk_per_grp;
379 if (!remainder)
380 bg_idx--;
381 }
382 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
383 bgd[bg_idx].free_blocks++;
384 fs->sb->free_blocks++;
385 /* journal backup */
386 if (prev_bg_bmap_idx != bg_idx) {
387 memset(journal_buffer, '\0', fs->blksz);
Frederic Leroy04735e92013-06-26 18:11:25 +0200388 status = ext4fs_devread((lbaint_t)bgd[bg_idx].block_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000389 fs->sect_perblk, 0, fs->blksz,
390 journal_buffer);
391 if (status == 0)
392 goto fail;
393
394 if (ext4fs_log_journal(journal_buffer,
395 bgd[bg_idx].block_id))
396 goto fail;
397 prev_bg_bmap_idx = bg_idx;
398 }
399 debug("tigp buffer itself releasing %ld\n", blknr);
400 }
401fail:
402 free(tib_start_addr);
403 free(tipb_start_addr);
404 free(journal_buffer);
405}
406
407static int ext4fs_delete_file(int inodeno)
408{
409 struct ext2_inode inode;
410 short status;
411 int i;
412 int remainder;
413 long int blknr;
414 int bg_idx;
415 int ibmap_idx;
416 char *read_buffer = NULL;
417 char *start_block_address = NULL;
418 unsigned int no_blocks;
419
420 static int prev_bg_bmap_idx = -1;
421 unsigned int inodes_per_block;
422 long int blkno;
423 unsigned int blkoff;
424 unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group;
425 unsigned int inode_per_grp = ext4fs_root->sblock.inodes_per_group;
426 struct ext2_inode *inode_buffer = NULL;
427 struct ext2_block_group *bgd = NULL;
428 struct ext_filesystem *fs = get_fs();
429 char *journal_buffer = zalloc(fs->blksz);
430 if (!journal_buffer)
431 return -ENOMEM;
432 /* get the block group descriptor table */
433 bgd = (struct ext2_block_group *)fs->gdtable;
434 status = ext4fs_read_inode(ext4fs_root, inodeno, &inode);
435 if (status == 0)
436 goto fail;
437
438 /* read the block no allocated to a file */
439 no_blocks = inode.size / fs->blksz;
440 if (inode.size % fs->blksz)
441 no_blocks++;
442
443 if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
444 struct ext2fs_node *node_inode =
445 zalloc(sizeof(struct ext2fs_node));
446 if (!node_inode)
447 goto fail;
448 node_inode->data = ext4fs_root;
449 node_inode->ino = inodeno;
450 node_inode->inode_read = 0;
451 memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode));
452
453 for (i = 0; i < no_blocks; i++) {
454 blknr = read_allocated_block(&(node_inode->inode), i);
455 if (fs->blksz != 1024) {
456 bg_idx = blknr / blk_per_grp;
457 } else {
458 bg_idx = blknr / blk_per_grp;
459 remainder = blknr % blk_per_grp;
460 if (!remainder)
461 bg_idx--;
462 }
463 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
464 bg_idx);
465 debug("EXT4_EXTENTS Block releasing %ld: %d\n",
466 blknr, bg_idx);
467
468 bgd[bg_idx].free_blocks++;
469 fs->sb->free_blocks++;
470
471 /* journal backup */
472 if (prev_bg_bmap_idx != bg_idx) {
473 status =
Frederic Leroy04735e92013-06-26 18:11:25 +0200474 ext4fs_devread((lbaint_t)
475 bgd[bg_idx].block_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000476 fs->sect_perblk, 0,
477 fs->blksz, journal_buffer);
478 if (status == 0)
479 goto fail;
480 if (ext4fs_log_journal(journal_buffer,
481 bgd[bg_idx].block_id))
482 goto fail;
483 prev_bg_bmap_idx = bg_idx;
484 }
485 }
486 if (node_inode) {
487 free(node_inode);
488 node_inode = NULL;
489 }
490 } else {
491
492 delete_single_indirect_block(&inode);
493 delete_double_indirect_block(&inode);
494 delete_triple_indirect_block(&inode);
495
496 /* read the block no allocated to a file */
497 no_blocks = inode.size / fs->blksz;
498 if (inode.size % fs->blksz)
499 no_blocks++;
500 for (i = 0; i < no_blocks; i++) {
501 blknr = read_allocated_block(&inode, i);
502 if (fs->blksz != 1024) {
503 bg_idx = blknr / blk_per_grp;
504 } else {
505 bg_idx = blknr / blk_per_grp;
506 remainder = blknr % blk_per_grp;
507 if (!remainder)
508 bg_idx--;
509 }
510 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
511 bg_idx);
512 debug("ActualB releasing %ld: %d\n", blknr, bg_idx);
513
514 bgd[bg_idx].free_blocks++;
515 fs->sb->free_blocks++;
516 /* journal backup */
517 if (prev_bg_bmap_idx != bg_idx) {
518 memset(journal_buffer, '\0', fs->blksz);
Frederic Leroy04735e92013-06-26 18:11:25 +0200519 status = ext4fs_devread((lbaint_t)
520 bgd[bg_idx].block_id
Simon Glass293d7fb2012-12-26 09:53:28 +0000521 * fs->sect_perblk,
522 0, fs->blksz,
523 journal_buffer);
524 if (status == 0)
525 goto fail;
526 if (ext4fs_log_journal(journal_buffer,
527 bgd[bg_idx].block_id))
528 goto fail;
529 prev_bg_bmap_idx = bg_idx;
530 }
531 }
532 }
533
534 /* from the inode no to blockno */
535 inodes_per_block = fs->blksz / fs->inodesz;
536 ibmap_idx = inodeno / inode_per_grp;
537
538 /* get the block no */
539 inodeno--;
540 blkno = __le32_to_cpu(bgd[ibmap_idx].inode_table_id) +
541 (inodeno % __le32_to_cpu(inode_per_grp)) / inodes_per_block;
542
543 /* get the offset of the inode */
544 blkoff = ((inodeno) % inodes_per_block) * fs->inodesz;
545
546 /* read the block no containing the inode */
547 read_buffer = zalloc(fs->blksz);
548 if (!read_buffer)
549 goto fail;
550 start_block_address = read_buffer;
Frederic Leroy04735e92013-06-26 18:11:25 +0200551 status = ext4fs_devread((lbaint_t)blkno * fs->sect_perblk,
Simon Glass293d7fb2012-12-26 09:53:28 +0000552 0, fs->blksz, read_buffer);
553 if (status == 0)
554 goto fail;
555
556 if (ext4fs_log_journal(read_buffer, blkno))
557 goto fail;
558
559 read_buffer = read_buffer + blkoff;
560 inode_buffer = (struct ext2_inode *)read_buffer;
561 memset(inode_buffer, '\0', sizeof(struct ext2_inode));
562
563 /* write the inode to original position in inode table */
564 if (ext4fs_put_metadata(start_block_address, blkno))
565 goto fail;
566
567 /* update the respective inode bitmaps */
568 inodeno++;
569 ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx);
570 bgd[ibmap_idx].free_inodes++;
571 fs->sb->free_inodes++;
572 /* journal backup */
573 memset(journal_buffer, '\0', fs->blksz);
Frederic Leroy04735e92013-06-26 18:11:25 +0200574 status = ext4fs_devread((lbaint_t)bgd[ibmap_idx].inode_id *
Simon Glass293d7fb2012-12-26 09:53:28 +0000575 fs->sect_perblk, 0, fs->blksz, journal_buffer);
576 if (status == 0)
577 goto fail;
578 if (ext4fs_log_journal(journal_buffer, bgd[ibmap_idx].inode_id))
579 goto fail;
580
581 ext4fs_update();
582 ext4fs_deinit();
583
584 if (ext4fs_init() != 0) {
585 printf("error in File System init\n");
586 goto fail;
587 }
588
589 free(start_block_address);
590 free(journal_buffer);
591
592 return 0;
593fail:
594 free(start_block_address);
595 free(journal_buffer);
596
597 return -1;
598}
599
600int ext4fs_init(void)
601{
602 short status;
603 int i;
604 unsigned int real_free_blocks = 0;
605 struct ext_filesystem *fs = get_fs();
606
607 /* populate fs */
608 fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root);
609 fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root);
Egbert Eich50ce4c02013-05-01 01:13:19 +0000610 fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz;
Simon Glass293d7fb2012-12-26 09:53:28 +0000611
612 /* get the superblock */
613 fs->sb = zalloc(SUPERBLOCK_SIZE);
614 if (!fs->sb)
615 return -ENOMEM;
Egbert Eich50ce4c02013-05-01 01:13:19 +0000616 if (!ext4_read_superblock((char *)fs->sb))
Simon Glass293d7fb2012-12-26 09:53:28 +0000617 goto fail;
618
619 /* init journal */
620 if (ext4fs_init_journal())
621 goto fail;
622
623 /* get total no of blockgroups */
624 fs->no_blkgrp = (uint32_t)ext4fs_div_roundup(
625 (ext4fs_root->sblock.total_blocks -
626 ext4fs_root->sblock.first_data_block),
627 ext4fs_root->sblock.blocks_per_group);
628
629 /* get the block group descriptor table */
630 fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1);
631 if (ext4fs_get_bgdtable() == -1) {
632 printf("Error in getting the block group descriptor table\n");
633 goto fail;
634 }
635 fs->bgd = (struct ext2_block_group *)fs->gdtable;
636
637 /* load all the available bitmap block of the partition */
638 fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *));
639 if (!fs->blk_bmaps)
640 goto fail;
641 for (i = 0; i < fs->no_blkgrp; i++) {
642 fs->blk_bmaps[i] = zalloc(fs->blksz);
643 if (!fs->blk_bmaps[i])
644 goto fail;
645 }
646
647 for (i = 0; i < fs->no_blkgrp; i++) {
648 status =
Frederic Leroy04735e92013-06-26 18:11:25 +0200649 ext4fs_devread((lbaint_t)fs->bgd[i].block_id *
650 fs->sect_perblk, 0,
Simon Glass293d7fb2012-12-26 09:53:28 +0000651 fs->blksz, (char *)fs->blk_bmaps[i]);
652 if (status == 0)
653 goto fail;
654 }
655
656 /* load all the available inode bitmap of the partition */
657 fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *));
658 if (!fs->inode_bmaps)
659 goto fail;
660 for (i = 0; i < fs->no_blkgrp; i++) {
661 fs->inode_bmaps[i] = zalloc(fs->blksz);
662 if (!fs->inode_bmaps[i])
663 goto fail;
664 }
665
666 for (i = 0; i < fs->no_blkgrp; i++) {
Frederic Leroy04735e92013-06-26 18:11:25 +0200667 status = ext4fs_devread((lbaint_t)fs->bgd[i].inode_id *
668 fs->sect_perblk,
Simon Glass293d7fb2012-12-26 09:53:28 +0000669 0, fs->blksz,
670 (char *)fs->inode_bmaps[i]);
671 if (status == 0)
672 goto fail;
673 }
674
675 /*
676 * check filesystem consistency with free blocks of file system
677 * some time we observed that superblock freeblocks does not match
678 * with the blockgroups freeblocks when improper
679 * reboot of a linux kernel
680 */
681 for (i = 0; i < fs->no_blkgrp; i++)
682 real_free_blocks = real_free_blocks + fs->bgd[i].free_blocks;
683 if (real_free_blocks != fs->sb->free_blocks)
684 fs->sb->free_blocks = real_free_blocks;
685
686 return 0;
687fail:
688 ext4fs_deinit();
689
690 return -1;
691}
692
693void ext4fs_deinit(void)
694{
695 int i;
696 struct ext2_inode inode_journal;
697 struct journal_superblock_t *jsb;
698 long int blknr;
699 struct ext_filesystem *fs = get_fs();
700
701 /* free journal */
702 char *temp_buff = zalloc(fs->blksz);
703 if (temp_buff) {
704 ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
705 &inode_journal);
706 blknr = read_allocated_block(&inode_journal,
707 EXT2_JOURNAL_SUPERBLOCK);
Frederic Leroy04735e92013-06-26 18:11:25 +0200708 ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
Simon Glass293d7fb2012-12-26 09:53:28 +0000709 temp_buff);
710 jsb = (struct journal_superblock_t *)temp_buff;
711 jsb->s_start = cpu_to_be32(0);
Ma Haijun05508702014-01-08 08:15:33 +0800712 put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +0000713 (struct journal_superblock_t *)temp_buff, fs->blksz);
714 free(temp_buff);
715 }
716 ext4fs_free_journal();
717
718 /* get the superblock */
Egbert Eich50ce4c02013-05-01 01:13:19 +0000719 ext4_read_superblock((char *)fs->sb);
Simon Glass293d7fb2012-12-26 09:53:28 +0000720 fs->sb->feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
721 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
722 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
723 free(fs->sb);
724 fs->sb = NULL;
725
726 if (fs->blk_bmaps) {
727 for (i = 0; i < fs->no_blkgrp; i++) {
728 free(fs->blk_bmaps[i]);
729 fs->blk_bmaps[i] = NULL;
730 }
731 free(fs->blk_bmaps);
732 fs->blk_bmaps = NULL;
733 }
734
735 if (fs->inode_bmaps) {
736 for (i = 0; i < fs->no_blkgrp; i++) {
737 free(fs->inode_bmaps[i]);
738 fs->inode_bmaps[i] = NULL;
739 }
740 free(fs->inode_bmaps);
741 fs->inode_bmaps = NULL;
742 }
743
744
745 free(fs->gdtable);
746 fs->gdtable = NULL;
747 fs->bgd = NULL;
748 /*
749 * reinitiliazed the global inode and
750 * block bitmap first execution check variables
751 */
752 fs->first_pass_ibmap = 0;
753 fs->first_pass_bbmap = 0;
754 fs->curr_inode_no = 0;
755 fs->curr_blkno = 0;
756}
757
758static int ext4fs_write_file(struct ext2_inode *file_inode,
759 int pos, unsigned int len, char *buf)
760{
761 int i;
762 int blockcnt;
Simon Glass293d7fb2012-12-26 09:53:28 +0000763 unsigned int filesize = __le32_to_cpu(file_inode->size);
764 struct ext_filesystem *fs = get_fs();
Egbert Eich50ce4c02013-05-01 01:13:19 +0000765 int log2blksz = fs->dev_desc->log2blksz;
766 int log2_fs_blocksize = LOG2_BLOCK_SIZE(ext4fs_root) - log2blksz;
Simon Glass293d7fb2012-12-26 09:53:28 +0000767 int previous_block_number = -1;
768 int delayed_start = 0;
769 int delayed_extent = 0;
770 int delayed_next = 0;
771 char *delayed_buf = NULL;
772
773 /* Adjust len so it we can't read past the end of the file. */
774 if (len > filesize)
775 len = filesize;
776
777 blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz;
778
779 for (i = pos / fs->blksz; i < blockcnt; i++) {
780 long int blknr;
781 int blockend = fs->blksz;
782 int skipfirst = 0;
783 blknr = read_allocated_block(file_inode, i);
784 if (blknr < 0)
785 return -1;
786
Egbert Eich50ce4c02013-05-01 01:13:19 +0000787 blknr = blknr << log2_fs_blocksize;
Simon Glass293d7fb2012-12-26 09:53:28 +0000788
789 if (blknr) {
790 if (previous_block_number != -1) {
791 if (delayed_next == blknr) {
792 delayed_extent += blockend;
Egbert Eich50ce4c02013-05-01 01:13:19 +0000793 delayed_next += blockend >> log2blksz;
Simon Glass293d7fb2012-12-26 09:53:28 +0000794 } else { /* spill */
Egbert Eich50ce4c02013-05-01 01:13:19 +0000795 put_ext4((uint64_t)
Ma Haijun05508702014-01-08 08:15:33 +0800796 ((uint64_t)delayed_start << log2blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +0000797 delayed_buf,
798 (uint32_t) delayed_extent);
799 previous_block_number = blknr;
800 delayed_start = blknr;
801 delayed_extent = blockend;
802 delayed_buf = buf;
803 delayed_next = blknr +
Egbert Eich50ce4c02013-05-01 01:13:19 +0000804 (blockend >> log2blksz);
Simon Glass293d7fb2012-12-26 09:53:28 +0000805 }
806 } else {
807 previous_block_number = blknr;
808 delayed_start = blknr;
809 delayed_extent = blockend;
810 delayed_buf = buf;
811 delayed_next = blknr +
Egbert Eich50ce4c02013-05-01 01:13:19 +0000812 (blockend >> log2blksz);
Simon Glass293d7fb2012-12-26 09:53:28 +0000813 }
814 } else {
815 if (previous_block_number != -1) {
816 /* spill */
Ma Haijun05508702014-01-08 08:15:33 +0800817 put_ext4((uint64_t) ((uint64_t)delayed_start <<
Egbert Eich50ce4c02013-05-01 01:13:19 +0000818 log2blksz),
819 delayed_buf,
Simon Glass293d7fb2012-12-26 09:53:28 +0000820 (uint32_t) delayed_extent);
821 previous_block_number = -1;
822 }
823 memset(buf, 0, fs->blksz - skipfirst);
824 }
825 buf += fs->blksz - skipfirst;
826 }
827 if (previous_block_number != -1) {
828 /* spill */
Ma Haijun05508702014-01-08 08:15:33 +0800829 put_ext4((uint64_t) ((uint64_t)delayed_start << log2blksz),
Simon Glass293d7fb2012-12-26 09:53:28 +0000830 delayed_buf, (uint32_t) delayed_extent);
831 previous_block_number = -1;
832 }
833
834 return len;
835}
836
837int ext4fs_write(const char *fname, unsigned char *buffer,
838 unsigned long sizebytes)
839{
840 int ret = 0;
841 struct ext2_inode *file_inode = NULL;
842 unsigned char *inode_buffer = NULL;
843 int parent_inodeno;
844 int inodeno;
845 time_t timestamp = 0;
846
847 uint64_t bytes_reqd_for_file;
848 unsigned int blks_reqd_for_file;
849 unsigned int blocks_remaining;
850 int existing_file_inodeno;
851 char *temp_ptr = NULL;
852 long int itable_blkno;
853 long int parent_itable_blkno;
854 long int blkoff;
855 struct ext2_sblock *sblock = &(ext4fs_root->sblock);
856 unsigned int inodes_per_block;
857 unsigned int ibmap_idx;
858 struct ext_filesystem *fs = get_fs();
859 ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
860 memset(filename, 0x00, sizeof(filename));
861
862 g_parent_inode = zalloc(sizeof(struct ext2_inode));
863 if (!g_parent_inode)
864 goto fail;
865
866 if (ext4fs_init() != 0) {
867 printf("error in File System init\n");
868 return -1;
869 }
870 inodes_per_block = fs->blksz / fs->inodesz;
871 parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE);
872 if (parent_inodeno == -1)
873 goto fail;
874 if (ext4fs_iget(parent_inodeno, g_parent_inode))
875 goto fail;
876 /* check if the filename is already present in root */
877 existing_file_inodeno = ext4fs_filename_check(filename);
878 if (existing_file_inodeno != -1) {
879 ret = ext4fs_delete_file(existing_file_inodeno);
880 fs->first_pass_bbmap = 0;
881 fs->curr_blkno = 0;
882
883 fs->first_pass_ibmap = 0;
884 fs->curr_inode_no = 0;
885 if (ret)
886 goto fail;
887 }
888 /* calucalate how many blocks required */
889 bytes_reqd_for_file = sizebytes;
890 blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz);
891 if (do_div(bytes_reqd_for_file, fs->blksz) != 0) {
892 blks_reqd_for_file++;
893 debug("total bytes for a file %u\n", blks_reqd_for_file);
894 }
895 blocks_remaining = blks_reqd_for_file;
896 /* test for available space in partition */
897 if (fs->sb->free_blocks < blks_reqd_for_file) {
898 printf("Not enough space on partition !!!\n");
899 goto fail;
900 }
901
902 ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG);
903 /* prepare file inode */
904 inode_buffer = zalloc(fs->inodesz);
905 if (!inode_buffer)
906 goto fail;
907 file_inode = (struct ext2_inode *)inode_buffer;
908 file_inode->mode = S_IFREG | S_IRWXU |
909 S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH;
910 /* ToDo: Update correct time */
911 file_inode->mtime = timestamp;
912 file_inode->atime = timestamp;
913 file_inode->ctime = timestamp;
914 file_inode->nlinks = 1;
915 file_inode->size = sizebytes;
916
917 /* Allocate data blocks */
918 ext4fs_allocate_blocks(file_inode, blocks_remaining,
919 &blks_reqd_for_file);
Egbert Eich50ce4c02013-05-01 01:13:19 +0000920 file_inode->blockcnt = (blks_reqd_for_file * fs->blksz) >>
921 fs->dev_desc->log2blksz;
Simon Glass293d7fb2012-12-26 09:53:28 +0000922
923 temp_ptr = zalloc(fs->blksz);
924 if (!temp_ptr)
925 goto fail;
926 ibmap_idx = inodeno / ext4fs_root->sblock.inodes_per_group;
927 inodeno--;
928 itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) +
929 (inodeno % __le32_to_cpu(sblock->inodes_per_group)) /
930 inodes_per_block;
931 blkoff = (inodeno % inodes_per_block) * fs->inodesz;
Frederic Leroy04735e92013-06-26 18:11:25 +0200932 ext4fs_devread((lbaint_t)itable_blkno * fs->sect_perblk, 0, fs->blksz,
933 temp_ptr);
Simon Glass293d7fb2012-12-26 09:53:28 +0000934 if (ext4fs_log_journal(temp_ptr, itable_blkno))
935 goto fail;
936
937 memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz);
938 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
939 goto fail;
940 /* copy the file content into data blocks */
941 if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) {
942 printf("Error in copying content\n");
943 goto fail;
944 }
945 ibmap_idx = parent_inodeno / ext4fs_root->sblock.inodes_per_group;
946 parent_inodeno--;
947 parent_itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) +
948 (parent_inodeno %
949 __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
950 blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz;
951 if (parent_itable_blkno != itable_blkno) {
952 memset(temp_ptr, '\0', fs->blksz);
Frederic Leroy04735e92013-06-26 18:11:25 +0200953 ext4fs_devread((lbaint_t)parent_itable_blkno * fs->sect_perblk,
Simon Glass293d7fb2012-12-26 09:53:28 +0000954 0, fs->blksz, temp_ptr);
955 if (ext4fs_log_journal(temp_ptr, parent_itable_blkno))
956 goto fail;
957
958 memcpy(temp_ptr + blkoff, g_parent_inode,
959 sizeof(struct ext2_inode));
960 if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno))
961 goto fail;
962 free(temp_ptr);
963 } else {
964 /*
965 * If parent and child fall in same inode table block
966 * both should be kept in 1 buffer
967 */
968 memcpy(temp_ptr + blkoff, g_parent_inode,
969 sizeof(struct ext2_inode));
970 gd_index--;
971 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
972 goto fail;
973 free(temp_ptr);
974 }
975 ext4fs_update();
976 ext4fs_deinit();
977
978 fs->first_pass_bbmap = 0;
979 fs->curr_blkno = 0;
980 fs->first_pass_ibmap = 0;
981 fs->curr_inode_no = 0;
982 free(inode_buffer);
983 free(g_parent_inode);
984 g_parent_inode = NULL;
985
986 return 0;
987fail:
988 ext4fs_deinit();
989 free(inode_buffer);
990 free(g_parent_inode);
991 g_parent_inode = NULL;
992
993 return -1;
994}