1293d7fbdSSimon Glass /* 2293d7fbdSSimon Glass * (C) Copyright 2011 - 2012 Samsung Electronics 3293d7fbdSSimon Glass * EXT4 filesystem implementation in Uboot by 4293d7fbdSSimon Glass * Uma Shankar <uma.shankar@samsung.com> 5293d7fbdSSimon Glass * Manjunatha C Achar <a.manjunatha@samsung.com> 6293d7fbdSSimon Glass * 7293d7fbdSSimon Glass * ext4ls and ext4load : Based on ext2 ls and load support in Uboot. 8293d7fbdSSimon Glass * Ext4 read optimization taken from Open-Moko 9293d7fbdSSimon Glass * Qi bootloader 10293d7fbdSSimon Glass * 11293d7fbdSSimon Glass * (C) Copyright 2004 12293d7fbdSSimon Glass * esd gmbh <www.esd-electronics.com> 13293d7fbdSSimon Glass * Reinhard Arlt <reinhard.arlt@esd-electronics.com> 14293d7fbdSSimon Glass * 15293d7fbdSSimon Glass * based on code from grub2 fs/ext2.c and fs/fshelp.c by 16293d7fbdSSimon Glass * GRUB -- GRand Unified Bootloader 17293d7fbdSSimon Glass * Copyright (C) 2003, 2004 Free Software Foundation, Inc. 18293d7fbdSSimon Glass * 19293d7fbdSSimon Glass * ext4write : Based on generic ext4 protocol. 20293d7fbdSSimon Glass * 211a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 22293d7fbdSSimon Glass */ 23293d7fbdSSimon Glass 24293d7fbdSSimon Glass 25293d7fbdSSimon Glass #include <common.h> 26cf92e05cSSimon Glass #include <memalign.h> 27293d7fbdSSimon Glass #include <linux/stat.h> 28293d7fbdSSimon Glass #include <div64.h> 29293d7fbdSSimon Glass #include "ext4_common.h" 30293d7fbdSSimon Glass 3158a9ecbaSMichael Walle static inline void ext4fs_sb_free_inodes_inc(struct ext2_sblock *sb) 3258a9ecbaSMichael Walle { 3358a9ecbaSMichael Walle sb->free_inodes = cpu_to_le32(le32_to_cpu(sb->free_inodes) + 1); 3458a9ecbaSMichael Walle } 3558a9ecbaSMichael Walle 3658a9ecbaSMichael Walle static inline void ext4fs_sb_free_blocks_inc(struct ext2_sblock *sb) 3758a9ecbaSMichael Walle { 3858a9ecbaSMichael Walle sb->free_blocks = cpu_to_le32(le32_to_cpu(sb->free_blocks) + 1); 3958a9ecbaSMichael Walle } 4058a9ecbaSMichael Walle 4158a9ecbaSMichael Walle static inline void ext4fs_bg_free_inodes_inc(struct ext2_block_group *bg) 4258a9ecbaSMichael Walle { 4358a9ecbaSMichael Walle bg->free_inodes = cpu_to_le16(le16_to_cpu(bg->free_inodes) + 1); 4458a9ecbaSMichael Walle } 4558a9ecbaSMichael Walle 4658a9ecbaSMichael Walle static inline void ext4fs_bg_free_blocks_inc(struct ext2_block_group *bg) 4758a9ecbaSMichael Walle { 4858a9ecbaSMichael Walle bg->free_blocks = cpu_to_le16(le16_to_cpu(bg->free_blocks) + 1); 4958a9ecbaSMichael Walle } 5058a9ecbaSMichael Walle 51293d7fbdSSimon Glass static void ext4fs_update(void) 52293d7fbdSSimon Glass { 53293d7fbdSSimon Glass short i; 54293d7fbdSSimon Glass ext4fs_update_journal(); 55293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 56293d7fbdSSimon Glass 57293d7fbdSSimon Glass /* update super block */ 58293d7fbdSSimon Glass put_ext4((uint64_t)(SUPERBLOCK_SIZE), 59293d7fbdSSimon Glass (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); 60293d7fbdSSimon Glass 61293d7fbdSSimon Glass /* update block groups */ 62293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 6358a9ecbaSMichael Walle fs->bgd[i].bg_checksum = cpu_to_le16(ext4fs_checksum_update(i)); 6458a9ecbaSMichael Walle put_ext4((uint64_t)le32_to_cpu(fs->bgd[i].block_id) * fs->blksz, 65293d7fbdSSimon Glass fs->blk_bmaps[i], fs->blksz); 66293d7fbdSSimon Glass } 67293d7fbdSSimon Glass 68293d7fbdSSimon Glass /* update inode table groups */ 69293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 7058a9ecbaSMichael Walle put_ext4((uint64_t)le32_to_cpu(fs->bgd[i].inode_id) * fs->blksz, 71293d7fbdSSimon Glass fs->inode_bmaps[i], fs->blksz); 72293d7fbdSSimon Glass } 73293d7fbdSSimon Glass 74293d7fbdSSimon Glass /* update the block group descriptor table */ 750550870bSMa Haijun put_ext4((uint64_t)((uint64_t)fs->gdtable_blkno * (uint64_t)fs->blksz), 76293d7fbdSSimon Glass (struct ext2_block_group *)fs->gdtable, 77293d7fbdSSimon Glass (fs->blksz * fs->no_blk_pergdt)); 78293d7fbdSSimon Glass 79293d7fbdSSimon Glass ext4fs_dump_metadata(); 80293d7fbdSSimon Glass 81293d7fbdSSimon Glass gindex = 0; 82293d7fbdSSimon Glass gd_index = 0; 83293d7fbdSSimon Glass } 84293d7fbdSSimon Glass 85293d7fbdSSimon Glass int ext4fs_get_bgdtable(void) 86293d7fbdSSimon Glass { 87293d7fbdSSimon Glass int status; 88293d7fbdSSimon Glass int grp_desc_size; 89293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 90293d7fbdSSimon Glass grp_desc_size = sizeof(struct ext2_block_group); 91293d7fbdSSimon Glass fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz; 92293d7fbdSSimon Glass if ((fs->no_blkgrp * grp_desc_size) % fs->blksz) 93293d7fbdSSimon Glass fs->no_blk_pergdt++; 94293d7fbdSSimon Glass 95293d7fbdSSimon Glass /* allocate memory for gdtable */ 96293d7fbdSSimon Glass fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt); 97293d7fbdSSimon Glass if (!fs->gdtable) 98293d7fbdSSimon Glass return -ENOMEM; 99293d7fbdSSimon Glass /* read the group descriptor table */ 10004735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)fs->gdtable_blkno * fs->sect_perblk, 10104735e9cSFrederic Leroy 0, fs->blksz * fs->no_blk_pergdt, fs->gdtable); 102293d7fbdSSimon Glass if (status == 0) 103293d7fbdSSimon Glass goto fail; 104293d7fbdSSimon Glass 105293d7fbdSSimon Glass if (ext4fs_log_gdt(fs->gdtable)) { 106293d7fbdSSimon Glass printf("Error in ext4fs_log_gdt\n"); 107293d7fbdSSimon Glass return -1; 108293d7fbdSSimon Glass } 109293d7fbdSSimon Glass 110293d7fbdSSimon Glass return 0; 111293d7fbdSSimon Glass fail: 112293d7fbdSSimon Glass free(fs->gdtable); 113293d7fbdSSimon Glass fs->gdtable = NULL; 114293d7fbdSSimon Glass 115293d7fbdSSimon Glass return -1; 116293d7fbdSSimon Glass } 117293d7fbdSSimon Glass 118293d7fbdSSimon Glass static void delete_single_indirect_block(struct ext2_inode *inode) 119293d7fbdSSimon Glass { 120293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 121293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 12258a9ecbaSMichael Walle uint32_t blknr; 123293d7fbdSSimon Glass int remainder; 124293d7fbdSSimon Glass int bg_idx; 125293d7fbdSSimon Glass int status; 12658a9ecbaSMichael Walle uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group); 127293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 128293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 129293d7fbdSSimon Glass if (!journal_buffer) { 130293d7fbdSSimon Glass printf("No memory\n"); 131293d7fbdSSimon Glass return; 132293d7fbdSSimon Glass } 133293d7fbdSSimon Glass /* get block group descriptor table */ 134293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 135293d7fbdSSimon Glass 136293d7fbdSSimon Glass /* deleting the single indirect block associated with inode */ 137293d7fbdSSimon Glass if (inode->b.blocks.indir_block != 0) { 13858a9ecbaSMichael Walle blknr = le32_to_cpu(inode->b.blocks.indir_block); 13958a9ecbaSMichael Walle debug("SIPB releasing %u\n", blknr); 140293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 14135dd055bSŁukasz Majewski if (fs->blksz == 1024) { 142293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 143293d7fbdSSimon Glass if (!remainder) 144293d7fbdSSimon Glass bg_idx--; 145293d7fbdSSimon Glass } 146293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 14758a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 14858a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 149293d7fbdSSimon Glass /* journal backup */ 150293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 15158a9ecbaSMichael Walle status = ext4fs_devread( 15258a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 153293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 154293d7fbdSSimon Glass journal_buffer); 155293d7fbdSSimon Glass if (status == 0) 156293d7fbdSSimon Glass goto fail; 157293d7fbdSSimon Glass if (ext4fs_log_journal 15858a9ecbaSMichael Walle (journal_buffer, le32_to_cpu(bgd[bg_idx].block_id))) 159293d7fbdSSimon Glass goto fail; 160293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 161293d7fbdSSimon Glass } 162293d7fbdSSimon Glass } 163293d7fbdSSimon Glass fail: 164293d7fbdSSimon Glass free(journal_buffer); 165293d7fbdSSimon Glass } 166293d7fbdSSimon Glass 167293d7fbdSSimon Glass static void delete_double_indirect_block(struct ext2_inode *inode) 168293d7fbdSSimon Glass { 169293d7fbdSSimon Glass int i; 170293d7fbdSSimon Glass short status; 171293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 17258a9ecbaSMichael Walle uint32_t blknr; 173293d7fbdSSimon Glass int remainder; 174293d7fbdSSimon Glass int bg_idx; 17558a9ecbaSMichael Walle uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group); 17658a9ecbaSMichael Walle __le32 *di_buffer = NULL; 17758a9ecbaSMichael Walle void *dib_start_addr = NULL; 178293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 179293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 180293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 181293d7fbdSSimon Glass if (!journal_buffer) { 182293d7fbdSSimon Glass printf("No memory\n"); 183293d7fbdSSimon Glass return; 184293d7fbdSSimon Glass } 185293d7fbdSSimon Glass /* get the block group descriptor table */ 186293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 187293d7fbdSSimon Glass 188293d7fbdSSimon Glass if (inode->b.blocks.double_indir_block != 0) { 189293d7fbdSSimon Glass di_buffer = zalloc(fs->blksz); 190293d7fbdSSimon Glass if (!di_buffer) { 191293d7fbdSSimon Glass printf("No memory\n"); 192293d7fbdSSimon Glass return; 193293d7fbdSSimon Glass } 19458a9ecbaSMichael Walle dib_start_addr = di_buffer; 19558a9ecbaSMichael Walle blknr = le32_to_cpu(inode->b.blocks.double_indir_block); 19604735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, 19704735e9cSFrederic Leroy fs->blksz, (char *)di_buffer); 198293d7fbdSSimon Glass for (i = 0; i < fs->blksz / sizeof(int); i++) { 199293d7fbdSSimon Glass if (*di_buffer == 0) 200293d7fbdSSimon Glass break; 201293d7fbdSSimon Glass 202293d7fbdSSimon Glass debug("DICB releasing %u\n", *di_buffer); 20358a9ecbaSMichael Walle bg_idx = le32_to_cpu(*di_buffer) / blk_per_grp; 20435dd055bSŁukasz Majewski if (fs->blksz == 1024) { 20558a9ecbaSMichael Walle remainder = le32_to_cpu(*di_buffer) % blk_per_grp; 206293d7fbdSSimon Glass if (!remainder) 207293d7fbdSSimon Glass bg_idx--; 208293d7fbdSSimon Glass } 20958a9ecbaSMichael Walle ext4fs_reset_block_bmap(le32_to_cpu(*di_buffer), 210293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], bg_idx); 211293d7fbdSSimon Glass di_buffer++; 21258a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 21358a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 214293d7fbdSSimon Glass /* journal backup */ 215293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 21658a9ecbaSMichael Walle status = ext4fs_devread( 21758a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) 218293d7fbdSSimon Glass * fs->sect_perblk, 0, 219293d7fbdSSimon Glass fs->blksz, 220293d7fbdSSimon Glass journal_buffer); 221293d7fbdSSimon Glass if (status == 0) 222293d7fbdSSimon Glass goto fail; 223293d7fbdSSimon Glass 224293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 22558a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 226293d7fbdSSimon Glass goto fail; 227293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 228293d7fbdSSimon Glass } 229293d7fbdSSimon Glass } 230293d7fbdSSimon Glass 231293d7fbdSSimon Glass /* removing the parent double indirect block */ 23258a9ecbaSMichael Walle blknr = le32_to_cpu(inode->b.blocks.double_indir_block); 233293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 23435dd055bSŁukasz Majewski if (fs->blksz == 1024) { 235293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 236293d7fbdSSimon Glass if (!remainder) 237293d7fbdSSimon Glass bg_idx--; 238293d7fbdSSimon Glass } 239293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 24058a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 24158a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 242293d7fbdSSimon Glass /* journal backup */ 243293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 244293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 24558a9ecbaSMichael Walle status = ext4fs_devread((lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 246293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 247293d7fbdSSimon Glass journal_buffer); 248293d7fbdSSimon Glass if (status == 0) 249293d7fbdSSimon Glass goto fail; 250293d7fbdSSimon Glass 251293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 25258a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 253293d7fbdSSimon Glass goto fail; 254293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 255293d7fbdSSimon Glass } 25658a9ecbaSMichael Walle debug("DIPB releasing %d\n", blknr); 257293d7fbdSSimon Glass } 258293d7fbdSSimon Glass fail: 25958a9ecbaSMichael Walle free(dib_start_addr); 260293d7fbdSSimon Glass free(journal_buffer); 261293d7fbdSSimon Glass } 262293d7fbdSSimon Glass 263293d7fbdSSimon Glass static void delete_triple_indirect_block(struct ext2_inode *inode) 264293d7fbdSSimon Glass { 265293d7fbdSSimon Glass int i, j; 266293d7fbdSSimon Glass short status; 267293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 26858a9ecbaSMichael Walle uint32_t blknr; 269293d7fbdSSimon Glass int remainder; 270293d7fbdSSimon Glass int bg_idx; 27158a9ecbaSMichael Walle uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group); 27258a9ecbaSMichael Walle __le32 *tigp_buffer = NULL; 27358a9ecbaSMichael Walle void *tib_start_addr = NULL; 27458a9ecbaSMichael Walle __le32 *tip_buffer = NULL; 27558a9ecbaSMichael Walle void *tipb_start_addr = NULL; 276293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 277293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 278293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 279293d7fbdSSimon Glass if (!journal_buffer) { 280293d7fbdSSimon Glass printf("No memory\n"); 281293d7fbdSSimon Glass return; 282293d7fbdSSimon Glass } 283293d7fbdSSimon Glass /* get block group descriptor table */ 284293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 285293d7fbdSSimon Glass 286293d7fbdSSimon Glass if (inode->b.blocks.triple_indir_block != 0) { 287293d7fbdSSimon Glass tigp_buffer = zalloc(fs->blksz); 288293d7fbdSSimon Glass if (!tigp_buffer) { 289293d7fbdSSimon Glass printf("No memory\n"); 290293d7fbdSSimon Glass return; 291293d7fbdSSimon Glass } 29258a9ecbaSMichael Walle tib_start_addr = tigp_buffer; 29358a9ecbaSMichael Walle blknr = le32_to_cpu(inode->b.blocks.triple_indir_block); 29404735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, 29504735e9cSFrederic Leroy fs->blksz, (char *)tigp_buffer); 296293d7fbdSSimon Glass for (i = 0; i < fs->blksz / sizeof(int); i++) { 297293d7fbdSSimon Glass if (*tigp_buffer == 0) 298293d7fbdSSimon Glass break; 299293d7fbdSSimon Glass debug("tigp buffer releasing %u\n", *tigp_buffer); 300293d7fbdSSimon Glass 301293d7fbdSSimon Glass tip_buffer = zalloc(fs->blksz); 302293d7fbdSSimon Glass if (!tip_buffer) 303293d7fbdSSimon Glass goto fail; 30458a9ecbaSMichael Walle tipb_start_addr = tip_buffer; 30558a9ecbaSMichael Walle status = ext4fs_devread((lbaint_t)le32_to_cpu(*tigp_buffer) * 306293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 307293d7fbdSSimon Glass (char *)tip_buffer); 308293d7fbdSSimon Glass for (j = 0; j < fs->blksz / sizeof(int); j++) { 30958a9ecbaSMichael Walle if (le32_to_cpu(*tip_buffer) == 0) 310293d7fbdSSimon Glass break; 31158a9ecbaSMichael Walle bg_idx = le32_to_cpu(*tip_buffer) / blk_per_grp; 31235dd055bSŁukasz Majewski if (fs->blksz == 1024) { 31358a9ecbaSMichael Walle remainder = le32_to_cpu(*tip_buffer) % blk_per_grp; 314293d7fbdSSimon Glass if (!remainder) 315293d7fbdSSimon Glass bg_idx--; 316293d7fbdSSimon Glass } 317293d7fbdSSimon Glass 31858a9ecbaSMichael Walle ext4fs_reset_block_bmap(le32_to_cpu(*tip_buffer), 319293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], 320293d7fbdSSimon Glass bg_idx); 321293d7fbdSSimon Glass 322293d7fbdSSimon Glass tip_buffer++; 32358a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 32458a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 325293d7fbdSSimon Glass /* journal backup */ 326293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 327293d7fbdSSimon Glass status = 328293d7fbdSSimon Glass ext4fs_devread( 32958a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 330293d7fbdSSimon Glass fs->sect_perblk, 0, 331293d7fbdSSimon Glass fs->blksz, 332293d7fbdSSimon Glass journal_buffer); 333293d7fbdSSimon Glass if (status == 0) 334293d7fbdSSimon Glass goto fail; 335293d7fbdSSimon Glass 336293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 33758a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 338293d7fbdSSimon Glass goto fail; 339293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 340293d7fbdSSimon Glass } 341293d7fbdSSimon Glass } 342293d7fbdSSimon Glass free(tipb_start_addr); 343293d7fbdSSimon Glass tipb_start_addr = NULL; 344293d7fbdSSimon Glass 345293d7fbdSSimon Glass /* 346293d7fbdSSimon Glass * removing the grand parent blocks 347293d7fbdSSimon Glass * which is connected to inode 348293d7fbdSSimon Glass */ 34958a9ecbaSMichael Walle bg_idx = le32_to_cpu(*tigp_buffer) / blk_per_grp; 35035dd055bSŁukasz Majewski if (fs->blksz == 1024) { 35158a9ecbaSMichael Walle remainder = le32_to_cpu(*tigp_buffer) % blk_per_grp; 352293d7fbdSSimon Glass if (!remainder) 353293d7fbdSSimon Glass bg_idx--; 354293d7fbdSSimon Glass } 35558a9ecbaSMichael Walle ext4fs_reset_block_bmap(le32_to_cpu(*tigp_buffer), 356293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], bg_idx); 357293d7fbdSSimon Glass 358293d7fbdSSimon Glass tigp_buffer++; 35958a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 36058a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 361293d7fbdSSimon Glass /* journal backup */ 362293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 363293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 364293d7fbdSSimon Glass status = 36558a9ecbaSMichael Walle ext4fs_devread( 36658a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 367293d7fbdSSimon Glass fs->sect_perblk, 0, 368293d7fbdSSimon Glass fs->blksz, journal_buffer); 369293d7fbdSSimon Glass if (status == 0) 370293d7fbdSSimon Glass goto fail; 371293d7fbdSSimon Glass 372293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 37358a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 374293d7fbdSSimon Glass goto fail; 375293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 376293d7fbdSSimon Glass } 377293d7fbdSSimon Glass } 378293d7fbdSSimon Glass 379293d7fbdSSimon Glass /* removing the grand parent triple indirect block */ 38058a9ecbaSMichael Walle blknr = le32_to_cpu(inode->b.blocks.triple_indir_block); 381293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 38235dd055bSŁukasz Majewski if (fs->blksz == 1024) { 383293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 384293d7fbdSSimon Glass if (!remainder) 385293d7fbdSSimon Glass bg_idx--; 386293d7fbdSSimon Glass } 387293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 38858a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 38958a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 390293d7fbdSSimon Glass /* journal backup */ 391293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 392293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 39358a9ecbaSMichael Walle status = ext4fs_devread( 39458a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 395293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 396293d7fbdSSimon Glass journal_buffer); 397293d7fbdSSimon Glass if (status == 0) 398293d7fbdSSimon Glass goto fail; 399293d7fbdSSimon Glass 400293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 40158a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 402293d7fbdSSimon Glass goto fail; 403293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 404293d7fbdSSimon Glass } 40558a9ecbaSMichael Walle debug("tigp buffer itself releasing %d\n", blknr); 406293d7fbdSSimon Glass } 407293d7fbdSSimon Glass fail: 408293d7fbdSSimon Glass free(tib_start_addr); 409293d7fbdSSimon Glass free(tipb_start_addr); 410293d7fbdSSimon Glass free(journal_buffer); 411293d7fbdSSimon Glass } 412293d7fbdSSimon Glass 413293d7fbdSSimon Glass static int ext4fs_delete_file(int inodeno) 414293d7fbdSSimon Glass { 415293d7fbdSSimon Glass struct ext2_inode inode; 416293d7fbdSSimon Glass short status; 417293d7fbdSSimon Glass int i; 418293d7fbdSSimon Glass int remainder; 419293d7fbdSSimon Glass long int blknr; 420293d7fbdSSimon Glass int bg_idx; 421293d7fbdSSimon Glass int ibmap_idx; 422293d7fbdSSimon Glass char *read_buffer = NULL; 423293d7fbdSSimon Glass char *start_block_address = NULL; 42458a9ecbaSMichael Walle uint32_t no_blocks; 425293d7fbdSSimon Glass 426293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 427293d7fbdSSimon Glass unsigned int inodes_per_block; 42858a9ecbaSMichael Walle uint32_t blkno; 429293d7fbdSSimon Glass unsigned int blkoff; 43058a9ecbaSMichael Walle uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group); 43158a9ecbaSMichael Walle uint32_t inode_per_grp = le32_to_cpu(ext4fs_root->sblock.inodes_per_group); 432293d7fbdSSimon Glass struct ext2_inode *inode_buffer = NULL; 433293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 434293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 435293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 436293d7fbdSSimon Glass if (!journal_buffer) 437293d7fbdSSimon Glass return -ENOMEM; 438293d7fbdSSimon Glass /* get the block group descriptor table */ 439293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 440293d7fbdSSimon Glass status = ext4fs_read_inode(ext4fs_root, inodeno, &inode); 441293d7fbdSSimon Glass if (status == 0) 442293d7fbdSSimon Glass goto fail; 443293d7fbdSSimon Glass 444293d7fbdSSimon Glass /* read the block no allocated to a file */ 44558a9ecbaSMichael Walle no_blocks = le32_to_cpu(inode.size) / fs->blksz; 44658a9ecbaSMichael Walle if (le32_to_cpu(inode.size) % fs->blksz) 447293d7fbdSSimon Glass no_blocks++; 448293d7fbdSSimon Glass 449293d7fbdSSimon Glass if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) { 450293d7fbdSSimon Glass struct ext2fs_node *node_inode = 451293d7fbdSSimon Glass zalloc(sizeof(struct ext2fs_node)); 452293d7fbdSSimon Glass if (!node_inode) 453293d7fbdSSimon Glass goto fail; 454293d7fbdSSimon Glass node_inode->data = ext4fs_root; 455293d7fbdSSimon Glass node_inode->ino = inodeno; 456293d7fbdSSimon Glass node_inode->inode_read = 0; 457293d7fbdSSimon Glass memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode)); 458293d7fbdSSimon Glass 459293d7fbdSSimon Glass for (i = 0; i < no_blocks; i++) { 460293d7fbdSSimon Glass blknr = read_allocated_block(&(node_inode->inode), i); 461293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 46235dd055bSŁukasz Majewski if (fs->blksz == 1024) { 463293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 464293d7fbdSSimon Glass if (!remainder) 465293d7fbdSSimon Glass bg_idx--; 466293d7fbdSSimon Glass } 467293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], 468293d7fbdSSimon Glass bg_idx); 469293d7fbdSSimon Glass debug("EXT4_EXTENTS Block releasing %ld: %d\n", 470293d7fbdSSimon Glass blknr, bg_idx); 471293d7fbdSSimon Glass 47258a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 47358a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 474293d7fbdSSimon Glass 475293d7fbdSSimon Glass /* journal backup */ 476293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 477293d7fbdSSimon Glass status = 47858a9ecbaSMichael Walle ext4fs_devread( 47958a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) * 480293d7fbdSSimon Glass fs->sect_perblk, 0, 481293d7fbdSSimon Glass fs->blksz, journal_buffer); 482293d7fbdSSimon Glass if (status == 0) 483293d7fbdSSimon Glass goto fail; 484293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 48558a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 486293d7fbdSSimon Glass goto fail; 487293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 488293d7fbdSSimon Glass } 489293d7fbdSSimon Glass } 490293d7fbdSSimon Glass if (node_inode) { 491293d7fbdSSimon Glass free(node_inode); 492293d7fbdSSimon Glass node_inode = NULL; 493293d7fbdSSimon Glass } 494293d7fbdSSimon Glass } else { 495293d7fbdSSimon Glass 496293d7fbdSSimon Glass delete_single_indirect_block(&inode); 497293d7fbdSSimon Glass delete_double_indirect_block(&inode); 498293d7fbdSSimon Glass delete_triple_indirect_block(&inode); 499293d7fbdSSimon Glass 500293d7fbdSSimon Glass /* read the block no allocated to a file */ 50158a9ecbaSMichael Walle no_blocks = le32_to_cpu(inode.size) / fs->blksz; 50258a9ecbaSMichael Walle if (le32_to_cpu(inode.size) % fs->blksz) 503293d7fbdSSimon Glass no_blocks++; 504293d7fbdSSimon Glass for (i = 0; i < no_blocks; i++) { 505293d7fbdSSimon Glass blknr = read_allocated_block(&inode, i); 506293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 50735dd055bSŁukasz Majewski if (fs->blksz == 1024) { 508293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 509293d7fbdSSimon Glass if (!remainder) 510293d7fbdSSimon Glass bg_idx--; 511293d7fbdSSimon Glass } 512293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], 513293d7fbdSSimon Glass bg_idx); 514293d7fbdSSimon Glass debug("ActualB releasing %ld: %d\n", blknr, bg_idx); 515293d7fbdSSimon Glass 51658a9ecbaSMichael Walle ext4fs_bg_free_blocks_inc(&bgd[bg_idx]); 51758a9ecbaSMichael Walle ext4fs_sb_free_blocks_inc(fs->sb); 518293d7fbdSSimon Glass /* journal backup */ 519293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 520293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 52158a9ecbaSMichael Walle status = ext4fs_devread( 52258a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) 523293d7fbdSSimon Glass * fs->sect_perblk, 524293d7fbdSSimon Glass 0, fs->blksz, 525293d7fbdSSimon Glass journal_buffer); 526293d7fbdSSimon Glass if (status == 0) 527293d7fbdSSimon Glass goto fail; 528293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 52958a9ecbaSMichael Walle le32_to_cpu(bgd[bg_idx].block_id))) 530293d7fbdSSimon Glass goto fail; 531293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 532293d7fbdSSimon Glass } 533293d7fbdSSimon Glass } 534293d7fbdSSimon Glass } 535293d7fbdSSimon Glass 536293d7fbdSSimon Glass /* from the inode no to blockno */ 537293d7fbdSSimon Glass inodes_per_block = fs->blksz / fs->inodesz; 538293d7fbdSSimon Glass ibmap_idx = inodeno / inode_per_grp; 539293d7fbdSSimon Glass 540293d7fbdSSimon Glass /* get the block no */ 541293d7fbdSSimon Glass inodeno--; 5427f101be3SMichael Walle blkno = le32_to_cpu(bgd[ibmap_idx].inode_table_id) + 54358a9ecbaSMichael Walle (inodeno % inode_per_grp) / inodes_per_block; 544293d7fbdSSimon Glass 545293d7fbdSSimon Glass /* get the offset of the inode */ 546293d7fbdSSimon Glass blkoff = ((inodeno) % inodes_per_block) * fs->inodesz; 547293d7fbdSSimon Glass 548293d7fbdSSimon Glass /* read the block no containing the inode */ 549293d7fbdSSimon Glass read_buffer = zalloc(fs->blksz); 550293d7fbdSSimon Glass if (!read_buffer) 551293d7fbdSSimon Glass goto fail; 552293d7fbdSSimon Glass start_block_address = read_buffer; 55304735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blkno * fs->sect_perblk, 554293d7fbdSSimon Glass 0, fs->blksz, read_buffer); 555293d7fbdSSimon Glass if (status == 0) 556293d7fbdSSimon Glass goto fail; 557293d7fbdSSimon Glass 558293d7fbdSSimon Glass if (ext4fs_log_journal(read_buffer, blkno)) 559293d7fbdSSimon Glass goto fail; 560293d7fbdSSimon Glass 561293d7fbdSSimon Glass read_buffer = read_buffer + blkoff; 562293d7fbdSSimon Glass inode_buffer = (struct ext2_inode *)read_buffer; 563*87f9fdc0SStefan Brüns memset(inode_buffer, '\0', fs->inodesz); 564293d7fbdSSimon Glass 565293d7fbdSSimon Glass /* write the inode to original position in inode table */ 566293d7fbdSSimon Glass if (ext4fs_put_metadata(start_block_address, blkno)) 567293d7fbdSSimon Glass goto fail; 568293d7fbdSSimon Glass 569293d7fbdSSimon Glass /* update the respective inode bitmaps */ 570293d7fbdSSimon Glass inodeno++; 571293d7fbdSSimon Glass ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx); 57258a9ecbaSMichael Walle ext4fs_bg_free_inodes_inc(&bgd[ibmap_idx]); 57358a9ecbaSMichael Walle ext4fs_sb_free_inodes_inc(fs->sb); 574293d7fbdSSimon Glass /* journal backup */ 575293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 57658a9ecbaSMichael Walle status = ext4fs_devread((lbaint_t)le32_to_cpu(bgd[ibmap_idx].inode_id) * 577293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, journal_buffer); 578293d7fbdSSimon Glass if (status == 0) 579293d7fbdSSimon Glass goto fail; 58058a9ecbaSMichael Walle if (ext4fs_log_journal(journal_buffer, le32_to_cpu(bgd[ibmap_idx].inode_id))) 581293d7fbdSSimon Glass goto fail; 582293d7fbdSSimon Glass 583293d7fbdSSimon Glass ext4fs_update(); 584293d7fbdSSimon Glass ext4fs_deinit(); 5858b454eeeSŁukasz Majewski ext4fs_reinit_global(); 586293d7fbdSSimon Glass 587293d7fbdSSimon Glass if (ext4fs_init() != 0) { 588293d7fbdSSimon Glass printf("error in File System init\n"); 589293d7fbdSSimon Glass goto fail; 590293d7fbdSSimon Glass } 591293d7fbdSSimon Glass 592293d7fbdSSimon Glass free(start_block_address); 593293d7fbdSSimon Glass free(journal_buffer); 594293d7fbdSSimon Glass 595293d7fbdSSimon Glass return 0; 596293d7fbdSSimon Glass fail: 597293d7fbdSSimon Glass free(start_block_address); 598293d7fbdSSimon Glass free(journal_buffer); 599293d7fbdSSimon Glass 600293d7fbdSSimon Glass return -1; 601293d7fbdSSimon Glass } 602293d7fbdSSimon Glass 603293d7fbdSSimon Glass int ext4fs_init(void) 604293d7fbdSSimon Glass { 605293d7fbdSSimon Glass short status; 606293d7fbdSSimon Glass int i; 60758a9ecbaSMichael Walle uint32_t real_free_blocks = 0; 608293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 609293d7fbdSSimon Glass 610293d7fbdSSimon Glass /* populate fs */ 611293d7fbdSSimon Glass fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root); 61250ce4c07SEgbert Eich fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz; 613293d7fbdSSimon Glass 614293d7fbdSSimon Glass /* get the superblock */ 615293d7fbdSSimon Glass fs->sb = zalloc(SUPERBLOCK_SIZE); 616293d7fbdSSimon Glass if (!fs->sb) 617293d7fbdSSimon Glass return -ENOMEM; 61850ce4c07SEgbert Eich if (!ext4_read_superblock((char *)fs->sb)) 619293d7fbdSSimon Glass goto fail; 620293d7fbdSSimon Glass 621293d7fbdSSimon Glass /* init journal */ 622293d7fbdSSimon Glass if (ext4fs_init_journal()) 623293d7fbdSSimon Glass goto fail; 624293d7fbdSSimon Glass 625293d7fbdSSimon Glass /* get total no of blockgroups */ 626293d7fbdSSimon Glass fs->no_blkgrp = (uint32_t)ext4fs_div_roundup( 62758a9ecbaSMichael Walle le32_to_cpu(ext4fs_root->sblock.total_blocks) 62858a9ecbaSMichael Walle - le32_to_cpu(ext4fs_root->sblock.first_data_block), 62958a9ecbaSMichael Walle le32_to_cpu(ext4fs_root->sblock.blocks_per_group)); 630293d7fbdSSimon Glass 631293d7fbdSSimon Glass /* get the block group descriptor table */ 632293d7fbdSSimon Glass fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1); 633293d7fbdSSimon Glass if (ext4fs_get_bgdtable() == -1) { 634293d7fbdSSimon Glass printf("Error in getting the block group descriptor table\n"); 635293d7fbdSSimon Glass goto fail; 636293d7fbdSSimon Glass } 637293d7fbdSSimon Glass fs->bgd = (struct ext2_block_group *)fs->gdtable; 638293d7fbdSSimon Glass 639293d7fbdSSimon Glass /* load all the available bitmap block of the partition */ 640293d7fbdSSimon Glass fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *)); 641293d7fbdSSimon Glass if (!fs->blk_bmaps) 642293d7fbdSSimon Glass goto fail; 643293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 644293d7fbdSSimon Glass fs->blk_bmaps[i] = zalloc(fs->blksz); 645293d7fbdSSimon Glass if (!fs->blk_bmaps[i]) 646293d7fbdSSimon Glass goto fail; 647293d7fbdSSimon Glass } 648293d7fbdSSimon Glass 649293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 650293d7fbdSSimon Glass status = 65158a9ecbaSMichael Walle ext4fs_devread( 65258a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(fs->bgd[i].block_id) * 65304735e9cSFrederic Leroy fs->sect_perblk, 0, 654293d7fbdSSimon Glass fs->blksz, (char *)fs->blk_bmaps[i]); 655293d7fbdSSimon Glass if (status == 0) 656293d7fbdSSimon Glass goto fail; 657293d7fbdSSimon Glass } 658293d7fbdSSimon Glass 659293d7fbdSSimon Glass /* load all the available inode bitmap of the partition */ 660293d7fbdSSimon Glass fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *)); 661293d7fbdSSimon Glass if (!fs->inode_bmaps) 662293d7fbdSSimon Glass goto fail; 663293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 664293d7fbdSSimon Glass fs->inode_bmaps[i] = zalloc(fs->blksz); 665293d7fbdSSimon Glass if (!fs->inode_bmaps[i]) 666293d7fbdSSimon Glass goto fail; 667293d7fbdSSimon Glass } 668293d7fbdSSimon Glass 669293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 67058a9ecbaSMichael Walle status = ext4fs_devread( 67158a9ecbaSMichael Walle (lbaint_t)le32_to_cpu(fs->bgd[i].inode_id) * 67204735e9cSFrederic Leroy fs->sect_perblk, 673293d7fbdSSimon Glass 0, fs->blksz, 674293d7fbdSSimon Glass (char *)fs->inode_bmaps[i]); 675293d7fbdSSimon Glass if (status == 0) 676293d7fbdSSimon Glass goto fail; 677293d7fbdSSimon Glass } 678293d7fbdSSimon Glass 679293d7fbdSSimon Glass /* 680293d7fbdSSimon Glass * check filesystem consistency with free blocks of file system 681293d7fbdSSimon Glass * some time we observed that superblock freeblocks does not match 682293d7fbdSSimon Glass * with the blockgroups freeblocks when improper 683293d7fbdSSimon Glass * reboot of a linux kernel 684293d7fbdSSimon Glass */ 685293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) 68658a9ecbaSMichael Walle real_free_blocks = real_free_blocks + le16_to_cpu(fs->bgd[i].free_blocks); 68758a9ecbaSMichael Walle if (real_free_blocks != le32_to_cpu(fs->sb->free_blocks)) 68858a9ecbaSMichael Walle fs->sb->free_blocks = cpu_to_le32(real_free_blocks); 689293d7fbdSSimon Glass 690293d7fbdSSimon Glass return 0; 691293d7fbdSSimon Glass fail: 692293d7fbdSSimon Glass ext4fs_deinit(); 693293d7fbdSSimon Glass 694293d7fbdSSimon Glass return -1; 695293d7fbdSSimon Glass } 696293d7fbdSSimon Glass 697293d7fbdSSimon Glass void ext4fs_deinit(void) 698293d7fbdSSimon Glass { 699293d7fbdSSimon Glass int i; 700293d7fbdSSimon Glass struct ext2_inode inode_journal; 701293d7fbdSSimon Glass struct journal_superblock_t *jsb; 70258a9ecbaSMichael Walle uint32_t blknr; 703293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 70458a9ecbaSMichael Walle uint32_t new_feature_incompat; 705293d7fbdSSimon Glass 706293d7fbdSSimon Glass /* free journal */ 707293d7fbdSSimon Glass char *temp_buff = zalloc(fs->blksz); 708293d7fbdSSimon Glass if (temp_buff) { 709293d7fbdSSimon Glass ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, 710293d7fbdSSimon Glass &inode_journal); 711293d7fbdSSimon Glass blknr = read_allocated_block(&inode_journal, 712293d7fbdSSimon Glass EXT2_JOURNAL_SUPERBLOCK); 71304735e9cSFrederic Leroy ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz, 714293d7fbdSSimon Glass temp_buff); 715293d7fbdSSimon Glass jsb = (struct journal_superblock_t *)temp_buff; 71658a9ecbaSMichael Walle jsb->s_start = 0; 7170550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), 718293d7fbdSSimon Glass (struct journal_superblock_t *)temp_buff, fs->blksz); 719293d7fbdSSimon Glass free(temp_buff); 720293d7fbdSSimon Glass } 721293d7fbdSSimon Glass ext4fs_free_journal(); 722293d7fbdSSimon Glass 723293d7fbdSSimon Glass /* get the superblock */ 72450ce4c07SEgbert Eich ext4_read_superblock((char *)fs->sb); 72558a9ecbaSMichael Walle new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat); 72658a9ecbaSMichael Walle new_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; 72758a9ecbaSMichael Walle fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat); 728293d7fbdSSimon Glass put_ext4((uint64_t)(SUPERBLOCK_SIZE), 729293d7fbdSSimon Glass (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); 730293d7fbdSSimon Glass free(fs->sb); 731293d7fbdSSimon Glass fs->sb = NULL; 732293d7fbdSSimon Glass 733293d7fbdSSimon Glass if (fs->blk_bmaps) { 734293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 735293d7fbdSSimon Glass free(fs->blk_bmaps[i]); 736293d7fbdSSimon Glass fs->blk_bmaps[i] = NULL; 737293d7fbdSSimon Glass } 738293d7fbdSSimon Glass free(fs->blk_bmaps); 739293d7fbdSSimon Glass fs->blk_bmaps = NULL; 740293d7fbdSSimon Glass } 741293d7fbdSSimon Glass 742293d7fbdSSimon Glass if (fs->inode_bmaps) { 743293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 744293d7fbdSSimon Glass free(fs->inode_bmaps[i]); 745293d7fbdSSimon Glass fs->inode_bmaps[i] = NULL; 746293d7fbdSSimon Glass } 747293d7fbdSSimon Glass free(fs->inode_bmaps); 748293d7fbdSSimon Glass fs->inode_bmaps = NULL; 749293d7fbdSSimon Glass } 750293d7fbdSSimon Glass 751293d7fbdSSimon Glass 752293d7fbdSSimon Glass free(fs->gdtable); 753293d7fbdSSimon Glass fs->gdtable = NULL; 754293d7fbdSSimon Glass fs->bgd = NULL; 755293d7fbdSSimon Glass /* 756293d7fbdSSimon Glass * reinitiliazed the global inode and 757293d7fbdSSimon Glass * block bitmap first execution check variables 758293d7fbdSSimon Glass */ 759293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 760293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 761293d7fbdSSimon Glass fs->curr_inode_no = 0; 762293d7fbdSSimon Glass fs->curr_blkno = 0; 763293d7fbdSSimon Glass } 764293d7fbdSSimon Glass 765293d7fbdSSimon Glass static int ext4fs_write_file(struct ext2_inode *file_inode, 766293d7fbdSSimon Glass int pos, unsigned int len, char *buf) 767293d7fbdSSimon Glass { 768293d7fbdSSimon Glass int i; 769293d7fbdSSimon Glass int blockcnt; 77058a9ecbaSMichael Walle uint32_t filesize = le32_to_cpu(file_inode->size); 771293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 77250ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz; 77350ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(ext4fs_root) - log2blksz; 774293d7fbdSSimon Glass int previous_block_number = -1; 775293d7fbdSSimon Glass int delayed_start = 0; 776293d7fbdSSimon Glass int delayed_extent = 0; 777293d7fbdSSimon Glass int delayed_next = 0; 778293d7fbdSSimon Glass char *delayed_buf = NULL; 779293d7fbdSSimon Glass 780293d7fbdSSimon Glass /* Adjust len so it we can't read past the end of the file. */ 781293d7fbdSSimon Glass if (len > filesize) 782293d7fbdSSimon Glass len = filesize; 783293d7fbdSSimon Glass 784293d7fbdSSimon Glass blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz; 785293d7fbdSSimon Glass 786293d7fbdSSimon Glass for (i = pos / fs->blksz; i < blockcnt; i++) { 787293d7fbdSSimon Glass long int blknr; 788293d7fbdSSimon Glass int blockend = fs->blksz; 789293d7fbdSSimon Glass int skipfirst = 0; 790293d7fbdSSimon Glass blknr = read_allocated_block(file_inode, i); 791293d7fbdSSimon Glass if (blknr < 0) 792293d7fbdSSimon Glass return -1; 793293d7fbdSSimon Glass 79450ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize; 795293d7fbdSSimon Glass 796293d7fbdSSimon Glass if (blknr) { 797293d7fbdSSimon Glass if (previous_block_number != -1) { 798293d7fbdSSimon Glass if (delayed_next == blknr) { 799293d7fbdSSimon Glass delayed_extent += blockend; 80050ce4c07SEgbert Eich delayed_next += blockend >> log2blksz; 801293d7fbdSSimon Glass } else { /* spill */ 80250ce4c07SEgbert Eich put_ext4((uint64_t) 8030550870bSMa Haijun ((uint64_t)delayed_start << log2blksz), 804293d7fbdSSimon Glass delayed_buf, 805293d7fbdSSimon Glass (uint32_t) delayed_extent); 806293d7fbdSSimon Glass previous_block_number = blknr; 807293d7fbdSSimon Glass delayed_start = blknr; 808293d7fbdSSimon Glass delayed_extent = blockend; 809293d7fbdSSimon Glass delayed_buf = buf; 810293d7fbdSSimon Glass delayed_next = blknr + 81150ce4c07SEgbert Eich (blockend >> log2blksz); 812293d7fbdSSimon Glass } 813293d7fbdSSimon Glass } else { 814293d7fbdSSimon Glass previous_block_number = blknr; 815293d7fbdSSimon Glass delayed_start = blknr; 816293d7fbdSSimon Glass delayed_extent = blockend; 817293d7fbdSSimon Glass delayed_buf = buf; 818293d7fbdSSimon Glass delayed_next = blknr + 81950ce4c07SEgbert Eich (blockend >> log2blksz); 820293d7fbdSSimon Glass } 821293d7fbdSSimon Glass } else { 822293d7fbdSSimon Glass if (previous_block_number != -1) { 823293d7fbdSSimon Glass /* spill */ 8240550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)delayed_start << 82550ce4c07SEgbert Eich log2blksz), 82650ce4c07SEgbert Eich delayed_buf, 827293d7fbdSSimon Glass (uint32_t) delayed_extent); 828293d7fbdSSimon Glass previous_block_number = -1; 829293d7fbdSSimon Glass } 830293d7fbdSSimon Glass memset(buf, 0, fs->blksz - skipfirst); 831293d7fbdSSimon Glass } 832293d7fbdSSimon Glass buf += fs->blksz - skipfirst; 833293d7fbdSSimon Glass } 834293d7fbdSSimon Glass if (previous_block_number != -1) { 835293d7fbdSSimon Glass /* spill */ 8360550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)delayed_start << log2blksz), 837293d7fbdSSimon Glass delayed_buf, (uint32_t) delayed_extent); 838293d7fbdSSimon Glass previous_block_number = -1; 839293d7fbdSSimon Glass } 840293d7fbdSSimon Glass 841293d7fbdSSimon Glass return len; 842293d7fbdSSimon Glass } 843293d7fbdSSimon Glass 844293d7fbdSSimon Glass int ext4fs_write(const char *fname, unsigned char *buffer, 845293d7fbdSSimon Glass unsigned long sizebytes) 846293d7fbdSSimon Glass { 847293d7fbdSSimon Glass int ret = 0; 848293d7fbdSSimon Glass struct ext2_inode *file_inode = NULL; 849293d7fbdSSimon Glass unsigned char *inode_buffer = NULL; 850293d7fbdSSimon Glass int parent_inodeno; 851293d7fbdSSimon Glass int inodeno; 852293d7fbdSSimon Glass time_t timestamp = 0; 853293d7fbdSSimon Glass 854293d7fbdSSimon Glass uint64_t bytes_reqd_for_file; 855293d7fbdSSimon Glass unsigned int blks_reqd_for_file; 856293d7fbdSSimon Glass unsigned int blocks_remaining; 857293d7fbdSSimon Glass int existing_file_inodeno; 858293d7fbdSSimon Glass char *temp_ptr = NULL; 859293d7fbdSSimon Glass long int itable_blkno; 860293d7fbdSSimon Glass long int parent_itable_blkno; 861293d7fbdSSimon Glass long int blkoff; 862293d7fbdSSimon Glass struct ext2_sblock *sblock = &(ext4fs_root->sblock); 863293d7fbdSSimon Glass unsigned int inodes_per_block; 864293d7fbdSSimon Glass unsigned int ibmap_idx; 865293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 866293d7fbdSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256); 86746a5707dSJeroen Hofstee memset(filename, 0x00, 256); 868293d7fbdSSimon Glass 869*87f9fdc0SStefan Brüns g_parent_inode = zalloc(fs->inodesz); 870293d7fbdSSimon Glass if (!g_parent_inode) 871293d7fbdSSimon Glass goto fail; 872293d7fbdSSimon Glass 873293d7fbdSSimon Glass if (ext4fs_init() != 0) { 874293d7fbdSSimon Glass printf("error in File System init\n"); 875293d7fbdSSimon Glass return -1; 876293d7fbdSSimon Glass } 877293d7fbdSSimon Glass inodes_per_block = fs->blksz / fs->inodesz; 878293d7fbdSSimon Glass parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE); 879293d7fbdSSimon Glass if (parent_inodeno == -1) 880293d7fbdSSimon Glass goto fail; 881293d7fbdSSimon Glass if (ext4fs_iget(parent_inodeno, g_parent_inode)) 882293d7fbdSSimon Glass goto fail; 88310a7a1b8SStefan Brüns /* do not mess up a directory using hash trees */ 88410a7a1b8SStefan Brüns if (le32_to_cpu(g_parent_inode->flags) & EXT4_INDEX_FL) { 88510a7a1b8SStefan Brüns printf("hash tree directory\n"); 88610a7a1b8SStefan Brüns goto fail; 88710a7a1b8SStefan Brüns } 888293d7fbdSSimon Glass /* check if the filename is already present in root */ 88976a29519SStefan Brüns existing_file_inodeno = ext4fs_filename_unlink(filename); 890293d7fbdSSimon Glass if (existing_file_inodeno != -1) { 891293d7fbdSSimon Glass ret = ext4fs_delete_file(existing_file_inodeno); 892293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 893293d7fbdSSimon Glass fs->curr_blkno = 0; 894293d7fbdSSimon Glass 895293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 896293d7fbdSSimon Glass fs->curr_inode_no = 0; 897293d7fbdSSimon Glass if (ret) 898293d7fbdSSimon Glass goto fail; 899293d7fbdSSimon Glass } 900293d7fbdSSimon Glass /* calucalate how many blocks required */ 901293d7fbdSSimon Glass bytes_reqd_for_file = sizebytes; 902293d7fbdSSimon Glass blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz); 903293d7fbdSSimon Glass if (do_div(bytes_reqd_for_file, fs->blksz) != 0) { 904293d7fbdSSimon Glass blks_reqd_for_file++; 905293d7fbdSSimon Glass debug("total bytes for a file %u\n", blks_reqd_for_file); 906293d7fbdSSimon Glass } 907293d7fbdSSimon Glass blocks_remaining = blks_reqd_for_file; 908293d7fbdSSimon Glass /* test for available space in partition */ 90958a9ecbaSMichael Walle if (le32_to_cpu(fs->sb->free_blocks) < blks_reqd_for_file) { 910293d7fbdSSimon Glass printf("Not enough space on partition !!!\n"); 911293d7fbdSSimon Glass goto fail; 912293d7fbdSSimon Glass } 913293d7fbdSSimon Glass 914a0d767e2SStefan Brüns inodeno = ext4fs_update_parent_dentry(filename, FILETYPE_REG); 915a0d767e2SStefan Brüns if (inodeno == -1) 916a0d767e2SStefan Brüns goto fail; 917293d7fbdSSimon Glass /* prepare file inode */ 918293d7fbdSSimon Glass inode_buffer = zalloc(fs->inodesz); 919293d7fbdSSimon Glass if (!inode_buffer) 920293d7fbdSSimon Glass goto fail; 921293d7fbdSSimon Glass file_inode = (struct ext2_inode *)inode_buffer; 92258a9ecbaSMichael Walle file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | 92358a9ecbaSMichael Walle S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH); 924293d7fbdSSimon Glass /* ToDo: Update correct time */ 92558a9ecbaSMichael Walle file_inode->mtime = cpu_to_le32(timestamp); 92658a9ecbaSMichael Walle file_inode->atime = cpu_to_le32(timestamp); 92758a9ecbaSMichael Walle file_inode->ctime = cpu_to_le32(timestamp); 92858a9ecbaSMichael Walle file_inode->nlinks = cpu_to_le16(1); 92958a9ecbaSMichael Walle file_inode->size = cpu_to_le32(sizebytes); 930293d7fbdSSimon Glass 931293d7fbdSSimon Glass /* Allocate data blocks */ 932293d7fbdSSimon Glass ext4fs_allocate_blocks(file_inode, blocks_remaining, 933293d7fbdSSimon Glass &blks_reqd_for_file); 93458a9ecbaSMichael Walle file_inode->blockcnt = cpu_to_le32((blks_reqd_for_file * fs->blksz) >> 93558a9ecbaSMichael Walle fs->dev_desc->log2blksz); 936293d7fbdSSimon Glass 937293d7fbdSSimon Glass temp_ptr = zalloc(fs->blksz); 938293d7fbdSSimon Glass if (!temp_ptr) 939293d7fbdSSimon Glass goto fail; 94058a9ecbaSMichael Walle ibmap_idx = inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group); 941293d7fbdSSimon Glass inodeno--; 9427f101be3SMichael Walle itable_blkno = le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) + 9437f101be3SMichael Walle (inodeno % le32_to_cpu(sblock->inodes_per_group)) / 944293d7fbdSSimon Glass inodes_per_block; 945293d7fbdSSimon Glass blkoff = (inodeno % inodes_per_block) * fs->inodesz; 94604735e9cSFrederic Leroy ext4fs_devread((lbaint_t)itable_blkno * fs->sect_perblk, 0, fs->blksz, 94704735e9cSFrederic Leroy temp_ptr); 948293d7fbdSSimon Glass if (ext4fs_log_journal(temp_ptr, itable_blkno)) 949293d7fbdSSimon Glass goto fail; 950293d7fbdSSimon Glass 951293d7fbdSSimon Glass memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz); 952293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, itable_blkno)) 953293d7fbdSSimon Glass goto fail; 954293d7fbdSSimon Glass /* copy the file content into data blocks */ 955293d7fbdSSimon Glass if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) { 956293d7fbdSSimon Glass printf("Error in copying content\n"); 957293d7fbdSSimon Glass goto fail; 958293d7fbdSSimon Glass } 95958a9ecbaSMichael Walle ibmap_idx = parent_inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group); 960293d7fbdSSimon Glass parent_inodeno--; 9617f101be3SMichael Walle parent_itable_blkno = le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) + 962293d7fbdSSimon Glass (parent_inodeno % 9637f101be3SMichael Walle le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; 964293d7fbdSSimon Glass blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz; 965293d7fbdSSimon Glass if (parent_itable_blkno != itable_blkno) { 966293d7fbdSSimon Glass memset(temp_ptr, '\0', fs->blksz); 96704735e9cSFrederic Leroy ext4fs_devread((lbaint_t)parent_itable_blkno * fs->sect_perblk, 968293d7fbdSSimon Glass 0, fs->blksz, temp_ptr); 969293d7fbdSSimon Glass if (ext4fs_log_journal(temp_ptr, parent_itable_blkno)) 970293d7fbdSSimon Glass goto fail; 971293d7fbdSSimon Glass 972*87f9fdc0SStefan Brüns memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz); 973293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno)) 974293d7fbdSSimon Glass goto fail; 975293d7fbdSSimon Glass } else { 976293d7fbdSSimon Glass /* 977293d7fbdSSimon Glass * If parent and child fall in same inode table block 978293d7fbdSSimon Glass * both should be kept in 1 buffer 979293d7fbdSSimon Glass */ 980*87f9fdc0SStefan Brüns memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz); 981293d7fbdSSimon Glass gd_index--; 982293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, itable_blkno)) 983293d7fbdSSimon Glass goto fail; 984293d7fbdSSimon Glass } 985293d7fbdSSimon Glass ext4fs_update(); 986293d7fbdSSimon Glass ext4fs_deinit(); 987293d7fbdSSimon Glass 988293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 989293d7fbdSSimon Glass fs->curr_blkno = 0; 990293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 991293d7fbdSSimon Glass fs->curr_inode_no = 0; 992293d7fbdSSimon Glass free(inode_buffer); 993293d7fbdSSimon Glass free(g_parent_inode); 99487a40b6eSStefan Brüns free(temp_ptr); 995293d7fbdSSimon Glass g_parent_inode = NULL; 996293d7fbdSSimon Glass 997293d7fbdSSimon Glass return 0; 998293d7fbdSSimon Glass fail: 999293d7fbdSSimon Glass ext4fs_deinit(); 1000293d7fbdSSimon Glass free(inode_buffer); 1001293d7fbdSSimon Glass free(g_parent_inode); 100287a40b6eSStefan Brüns free(temp_ptr); 1003293d7fbdSSimon Glass g_parent_inode = NULL; 1004293d7fbdSSimon Glass 1005293d7fbdSSimon Glass return -1; 1006293d7fbdSSimon Glass } 10079f12cd0eSSuriyan Ramasami 10089f12cd0eSSuriyan Ramasami int ext4_write_file(const char *filename, void *buf, loff_t offset, 10099f12cd0eSSuriyan Ramasami loff_t len, loff_t *actwrite) 10109f12cd0eSSuriyan Ramasami { 10119f12cd0eSSuriyan Ramasami int ret; 10129f12cd0eSSuriyan Ramasami 10139f12cd0eSSuriyan Ramasami if (offset != 0) { 10149f12cd0eSSuriyan Ramasami printf("** Cannot support non-zero offset **\n"); 10159f12cd0eSSuriyan Ramasami return -1; 10169f12cd0eSSuriyan Ramasami } 10179f12cd0eSSuriyan Ramasami 10189f12cd0eSSuriyan Ramasami ret = ext4fs_write(filename, buf, len); 10199f12cd0eSSuriyan Ramasami if (ret) { 10209f12cd0eSSuriyan Ramasami printf("** Error ext4fs_write() **\n"); 10219f12cd0eSSuriyan Ramasami goto fail; 10229f12cd0eSSuriyan Ramasami } 10239f12cd0eSSuriyan Ramasami 102422b7509eSPrzemyslaw Marczak *actwrite = len; 102522b7509eSPrzemyslaw Marczak 10269f12cd0eSSuriyan Ramasami return 0; 10279f12cd0eSSuriyan Ramasami 10289f12cd0eSSuriyan Ramasami fail: 102922b7509eSPrzemyslaw Marczak *actwrite = 0; 10309f12cd0eSSuriyan Ramasami 10319f12cd0eSSuriyan Ramasami return -1; 10329f12cd0eSSuriyan Ramasami } 1033