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> 26*cf92e05cSSimon Glass #include <memalign.h> 27293d7fbdSSimon Glass #include <linux/stat.h> 28293d7fbdSSimon Glass #include <div64.h> 29293d7fbdSSimon Glass #include "ext4_common.h" 30293d7fbdSSimon Glass 31293d7fbdSSimon Glass static void ext4fs_update(void) 32293d7fbdSSimon Glass { 33293d7fbdSSimon Glass short i; 34293d7fbdSSimon Glass ext4fs_update_journal(); 35293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 36293d7fbdSSimon Glass 37293d7fbdSSimon Glass /* update super block */ 38293d7fbdSSimon Glass put_ext4((uint64_t)(SUPERBLOCK_SIZE), 39293d7fbdSSimon Glass (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); 40293d7fbdSSimon Glass 41293d7fbdSSimon Glass /* update block groups */ 42293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 43293d7fbdSSimon Glass fs->bgd[i].bg_checksum = ext4fs_checksum_update(i); 440550870bSMa Haijun put_ext4((uint64_t)((uint64_t)fs->bgd[i].block_id * (uint64_t)fs->blksz), 45293d7fbdSSimon Glass fs->blk_bmaps[i], fs->blksz); 46293d7fbdSSimon Glass } 47293d7fbdSSimon Glass 48293d7fbdSSimon Glass /* update inode table groups */ 49293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 500550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)fs->bgd[i].inode_id * (uint64_t)fs->blksz), 51293d7fbdSSimon Glass fs->inode_bmaps[i], fs->blksz); 52293d7fbdSSimon Glass } 53293d7fbdSSimon Glass 54293d7fbdSSimon Glass /* update the block group descriptor table */ 550550870bSMa Haijun put_ext4((uint64_t)((uint64_t)fs->gdtable_blkno * (uint64_t)fs->blksz), 56293d7fbdSSimon Glass (struct ext2_block_group *)fs->gdtable, 57293d7fbdSSimon Glass (fs->blksz * fs->no_blk_pergdt)); 58293d7fbdSSimon Glass 59293d7fbdSSimon Glass ext4fs_dump_metadata(); 60293d7fbdSSimon Glass 61293d7fbdSSimon Glass gindex = 0; 62293d7fbdSSimon Glass gd_index = 0; 63293d7fbdSSimon Glass } 64293d7fbdSSimon Glass 65293d7fbdSSimon Glass int ext4fs_get_bgdtable(void) 66293d7fbdSSimon Glass { 67293d7fbdSSimon Glass int status; 68293d7fbdSSimon Glass int grp_desc_size; 69293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 70293d7fbdSSimon Glass grp_desc_size = sizeof(struct ext2_block_group); 71293d7fbdSSimon Glass fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz; 72293d7fbdSSimon Glass if ((fs->no_blkgrp * grp_desc_size) % fs->blksz) 73293d7fbdSSimon Glass fs->no_blk_pergdt++; 74293d7fbdSSimon Glass 75293d7fbdSSimon Glass /* allocate memory for gdtable */ 76293d7fbdSSimon Glass fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt); 77293d7fbdSSimon Glass if (!fs->gdtable) 78293d7fbdSSimon Glass return -ENOMEM; 79293d7fbdSSimon Glass /* read the group descriptor table */ 8004735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)fs->gdtable_blkno * fs->sect_perblk, 8104735e9cSFrederic Leroy 0, fs->blksz * fs->no_blk_pergdt, fs->gdtable); 82293d7fbdSSimon Glass if (status == 0) 83293d7fbdSSimon Glass goto fail; 84293d7fbdSSimon Glass 85293d7fbdSSimon Glass if (ext4fs_log_gdt(fs->gdtable)) { 86293d7fbdSSimon Glass printf("Error in ext4fs_log_gdt\n"); 87293d7fbdSSimon Glass return -1; 88293d7fbdSSimon Glass } 89293d7fbdSSimon Glass 90293d7fbdSSimon Glass return 0; 91293d7fbdSSimon Glass fail: 92293d7fbdSSimon Glass free(fs->gdtable); 93293d7fbdSSimon Glass fs->gdtable = NULL; 94293d7fbdSSimon Glass 95293d7fbdSSimon Glass return -1; 96293d7fbdSSimon Glass } 97293d7fbdSSimon Glass 98293d7fbdSSimon Glass static void delete_single_indirect_block(struct ext2_inode *inode) 99293d7fbdSSimon Glass { 100293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 101293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 102293d7fbdSSimon Glass long int blknr; 103293d7fbdSSimon Glass int remainder; 104293d7fbdSSimon Glass int bg_idx; 105293d7fbdSSimon Glass int status; 106293d7fbdSSimon Glass unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; 107293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 108293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 109293d7fbdSSimon Glass if (!journal_buffer) { 110293d7fbdSSimon Glass printf("No memory\n"); 111293d7fbdSSimon Glass return; 112293d7fbdSSimon Glass } 113293d7fbdSSimon Glass /* get block group descriptor table */ 114293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 115293d7fbdSSimon Glass 116293d7fbdSSimon Glass /* deleting the single indirect block associated with inode */ 117293d7fbdSSimon Glass if (inode->b.blocks.indir_block != 0) { 118293d7fbdSSimon Glass debug("SIPB releasing %u\n", inode->b.blocks.indir_block); 119293d7fbdSSimon Glass blknr = inode->b.blocks.indir_block; 120293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 12135dd055bSŁukasz Majewski if (fs->blksz == 1024) { 122293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 123293d7fbdSSimon Glass if (!remainder) 124293d7fbdSSimon Glass bg_idx--; 125293d7fbdSSimon Glass } 126293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 127293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 128293d7fbdSSimon Glass fs->sb->free_blocks++; 129293d7fbdSSimon Glass /* journal backup */ 130293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 131293d7fbdSSimon Glass status = 13204735e9cSFrederic Leroy ext4fs_devread((lbaint_t)bgd[bg_idx].block_id * 133293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 134293d7fbdSSimon Glass journal_buffer); 135293d7fbdSSimon Glass if (status == 0) 136293d7fbdSSimon Glass goto fail; 137293d7fbdSSimon Glass if (ext4fs_log_journal 138293d7fbdSSimon Glass (journal_buffer, bgd[bg_idx].block_id)) 139293d7fbdSSimon Glass goto fail; 140293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 141293d7fbdSSimon Glass } 142293d7fbdSSimon Glass } 143293d7fbdSSimon Glass fail: 144293d7fbdSSimon Glass free(journal_buffer); 145293d7fbdSSimon Glass } 146293d7fbdSSimon Glass 147293d7fbdSSimon Glass static void delete_double_indirect_block(struct ext2_inode *inode) 148293d7fbdSSimon Glass { 149293d7fbdSSimon Glass int i; 150293d7fbdSSimon Glass short status; 151293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 152293d7fbdSSimon Glass long int blknr; 153293d7fbdSSimon Glass int remainder; 154293d7fbdSSimon Glass int bg_idx; 155293d7fbdSSimon Glass unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; 156293d7fbdSSimon Glass unsigned int *di_buffer = NULL; 157293d7fbdSSimon Glass unsigned int *DIB_start_addr = NULL; 158293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 159293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 160293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 161293d7fbdSSimon Glass if (!journal_buffer) { 162293d7fbdSSimon Glass printf("No memory\n"); 163293d7fbdSSimon Glass return; 164293d7fbdSSimon Glass } 165293d7fbdSSimon Glass /* get the block group descriptor table */ 166293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 167293d7fbdSSimon Glass 168293d7fbdSSimon Glass if (inode->b.blocks.double_indir_block != 0) { 169293d7fbdSSimon Glass di_buffer = zalloc(fs->blksz); 170293d7fbdSSimon Glass if (!di_buffer) { 171293d7fbdSSimon Glass printf("No memory\n"); 172293d7fbdSSimon Glass return; 173293d7fbdSSimon Glass } 174293d7fbdSSimon Glass DIB_start_addr = (unsigned int *)di_buffer; 175293d7fbdSSimon Glass blknr = inode->b.blocks.double_indir_block; 17604735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, 17704735e9cSFrederic Leroy fs->blksz, (char *)di_buffer); 178293d7fbdSSimon Glass for (i = 0; i < fs->blksz / sizeof(int); i++) { 179293d7fbdSSimon Glass if (*di_buffer == 0) 180293d7fbdSSimon Glass break; 181293d7fbdSSimon Glass 182293d7fbdSSimon Glass debug("DICB releasing %u\n", *di_buffer); 18335dd055bSŁukasz Majewski bg_idx = *di_buffer / blk_per_grp; 18435dd055bSŁukasz Majewski if (fs->blksz == 1024) { 18535dd055bSŁukasz Majewski remainder = *di_buffer % blk_per_grp; 186293d7fbdSSimon Glass if (!remainder) 187293d7fbdSSimon Glass bg_idx--; 188293d7fbdSSimon Glass } 189293d7fbdSSimon Glass ext4fs_reset_block_bmap(*di_buffer, 190293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], bg_idx); 191293d7fbdSSimon Glass di_buffer++; 192293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 193293d7fbdSSimon Glass fs->sb->free_blocks++; 194293d7fbdSSimon Glass /* journal backup */ 195293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 19604735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t) 19704735e9cSFrederic Leroy bgd[bg_idx].block_id 198293d7fbdSSimon Glass * fs->sect_perblk, 0, 199293d7fbdSSimon Glass fs->blksz, 200293d7fbdSSimon Glass journal_buffer); 201293d7fbdSSimon Glass if (status == 0) 202293d7fbdSSimon Glass goto fail; 203293d7fbdSSimon Glass 204293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 205293d7fbdSSimon Glass bgd[bg_idx].block_id)) 206293d7fbdSSimon Glass goto fail; 207293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 208293d7fbdSSimon Glass } 209293d7fbdSSimon Glass } 210293d7fbdSSimon Glass 211293d7fbdSSimon Glass /* removing the parent double indirect block */ 212293d7fbdSSimon Glass blknr = inode->b.blocks.double_indir_block; 213293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 21435dd055bSŁukasz Majewski if (fs->blksz == 1024) { 215293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 216293d7fbdSSimon Glass if (!remainder) 217293d7fbdSSimon Glass bg_idx--; 218293d7fbdSSimon Glass } 219293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 220293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 221293d7fbdSSimon Glass fs->sb->free_blocks++; 222293d7fbdSSimon Glass /* journal backup */ 223293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 224293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 22504735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)bgd[bg_idx].block_id * 226293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 227293d7fbdSSimon Glass journal_buffer); 228293d7fbdSSimon Glass if (status == 0) 229293d7fbdSSimon Glass goto fail; 230293d7fbdSSimon Glass 231293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 232293d7fbdSSimon Glass bgd[bg_idx].block_id)) 233293d7fbdSSimon Glass goto fail; 234293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 235293d7fbdSSimon Glass } 236293d7fbdSSimon Glass debug("DIPB releasing %ld\n", blknr); 237293d7fbdSSimon Glass } 238293d7fbdSSimon Glass fail: 239293d7fbdSSimon Glass free(DIB_start_addr); 240293d7fbdSSimon Glass free(journal_buffer); 241293d7fbdSSimon Glass } 242293d7fbdSSimon Glass 243293d7fbdSSimon Glass static void delete_triple_indirect_block(struct ext2_inode *inode) 244293d7fbdSSimon Glass { 245293d7fbdSSimon Glass int i, j; 246293d7fbdSSimon Glass short status; 247293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 248293d7fbdSSimon Glass long int blknr; 249293d7fbdSSimon Glass int remainder; 250293d7fbdSSimon Glass int bg_idx; 251293d7fbdSSimon Glass unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; 252293d7fbdSSimon Glass unsigned int *tigp_buffer = NULL; 253293d7fbdSSimon Glass unsigned int *tib_start_addr = NULL; 254293d7fbdSSimon Glass unsigned int *tip_buffer = NULL; 255293d7fbdSSimon Glass unsigned int *tipb_start_addr = NULL; 256293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 257293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 258293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 259293d7fbdSSimon Glass if (!journal_buffer) { 260293d7fbdSSimon Glass printf("No memory\n"); 261293d7fbdSSimon Glass return; 262293d7fbdSSimon Glass } 263293d7fbdSSimon Glass /* get block group descriptor table */ 264293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 265293d7fbdSSimon Glass 266293d7fbdSSimon Glass if (inode->b.blocks.triple_indir_block != 0) { 267293d7fbdSSimon Glass tigp_buffer = zalloc(fs->blksz); 268293d7fbdSSimon Glass if (!tigp_buffer) { 269293d7fbdSSimon Glass printf("No memory\n"); 270293d7fbdSSimon Glass return; 271293d7fbdSSimon Glass } 272293d7fbdSSimon Glass tib_start_addr = (unsigned int *)tigp_buffer; 273293d7fbdSSimon Glass blknr = inode->b.blocks.triple_indir_block; 27404735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, 27504735e9cSFrederic Leroy fs->blksz, (char *)tigp_buffer); 276293d7fbdSSimon Glass for (i = 0; i < fs->blksz / sizeof(int); i++) { 277293d7fbdSSimon Glass if (*tigp_buffer == 0) 278293d7fbdSSimon Glass break; 279293d7fbdSSimon Glass debug("tigp buffer releasing %u\n", *tigp_buffer); 280293d7fbdSSimon Glass 281293d7fbdSSimon Glass tip_buffer = zalloc(fs->blksz); 282293d7fbdSSimon Glass if (!tip_buffer) 283293d7fbdSSimon Glass goto fail; 284293d7fbdSSimon Glass tipb_start_addr = (unsigned int *)tip_buffer; 28504735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)(*tigp_buffer) * 286293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 287293d7fbdSSimon Glass (char *)tip_buffer); 288293d7fbdSSimon Glass for (j = 0; j < fs->blksz / sizeof(int); j++) { 289293d7fbdSSimon Glass if (*tip_buffer == 0) 290293d7fbdSSimon Glass break; 29135dd055bSŁukasz Majewski bg_idx = *tip_buffer / blk_per_grp; 29235dd055bSŁukasz Majewski if (fs->blksz == 1024) { 29335dd055bSŁukasz Majewski remainder = *tip_buffer % blk_per_grp; 294293d7fbdSSimon Glass if (!remainder) 295293d7fbdSSimon Glass bg_idx--; 296293d7fbdSSimon Glass } 297293d7fbdSSimon Glass 298293d7fbdSSimon Glass ext4fs_reset_block_bmap(*tip_buffer, 299293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], 300293d7fbdSSimon Glass bg_idx); 301293d7fbdSSimon Glass 302293d7fbdSSimon Glass tip_buffer++; 303293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 304293d7fbdSSimon Glass fs->sb->free_blocks++; 305293d7fbdSSimon Glass /* journal backup */ 306293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 307293d7fbdSSimon Glass status = 308293d7fbdSSimon Glass ext4fs_devread( 30904735e9cSFrederic Leroy (lbaint_t) 310293d7fbdSSimon Glass bgd[bg_idx].block_id * 311293d7fbdSSimon Glass fs->sect_perblk, 0, 312293d7fbdSSimon Glass fs->blksz, 313293d7fbdSSimon Glass journal_buffer); 314293d7fbdSSimon Glass if (status == 0) 315293d7fbdSSimon Glass goto fail; 316293d7fbdSSimon Glass 317293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 318293d7fbdSSimon Glass bgd[bg_idx]. 319293d7fbdSSimon Glass block_id)) 320293d7fbdSSimon Glass goto fail; 321293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 322293d7fbdSSimon Glass } 323293d7fbdSSimon Glass } 324293d7fbdSSimon Glass free(tipb_start_addr); 325293d7fbdSSimon Glass tipb_start_addr = NULL; 326293d7fbdSSimon Glass 327293d7fbdSSimon Glass /* 328293d7fbdSSimon Glass * removing the grand parent blocks 329293d7fbdSSimon Glass * which is connected to inode 330293d7fbdSSimon Glass */ 33135dd055bSŁukasz Majewski bg_idx = *tigp_buffer / blk_per_grp; 33235dd055bSŁukasz Majewski if (fs->blksz == 1024) { 33335dd055bSŁukasz Majewski remainder = *tigp_buffer % blk_per_grp; 334293d7fbdSSimon Glass if (!remainder) 335293d7fbdSSimon Glass bg_idx--; 336293d7fbdSSimon Glass } 337293d7fbdSSimon Glass ext4fs_reset_block_bmap(*tigp_buffer, 338293d7fbdSSimon Glass fs->blk_bmaps[bg_idx], bg_idx); 339293d7fbdSSimon Glass 340293d7fbdSSimon Glass tigp_buffer++; 341293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 342293d7fbdSSimon Glass fs->sb->free_blocks++; 343293d7fbdSSimon Glass /* journal backup */ 344293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 345293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 346293d7fbdSSimon Glass status = 34704735e9cSFrederic Leroy ext4fs_devread((lbaint_t) 34804735e9cSFrederic Leroy bgd[bg_idx].block_id * 349293d7fbdSSimon Glass fs->sect_perblk, 0, 350293d7fbdSSimon Glass fs->blksz, journal_buffer); 351293d7fbdSSimon Glass if (status == 0) 352293d7fbdSSimon Glass goto fail; 353293d7fbdSSimon Glass 354293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 355293d7fbdSSimon Glass bgd[bg_idx].block_id)) 356293d7fbdSSimon Glass goto fail; 357293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 358293d7fbdSSimon Glass } 359293d7fbdSSimon Glass } 360293d7fbdSSimon Glass 361293d7fbdSSimon Glass /* removing the grand parent triple indirect block */ 362293d7fbdSSimon Glass blknr = inode->b.blocks.triple_indir_block; 363293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 36435dd055bSŁukasz Majewski if (fs->blksz == 1024) { 365293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 366293d7fbdSSimon Glass if (!remainder) 367293d7fbdSSimon Glass bg_idx--; 368293d7fbdSSimon Glass } 369293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); 370293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 371293d7fbdSSimon Glass fs->sb->free_blocks++; 372293d7fbdSSimon Glass /* journal backup */ 373293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 374293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 37504735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)bgd[bg_idx].block_id * 376293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, 377293d7fbdSSimon Glass journal_buffer); 378293d7fbdSSimon Glass if (status == 0) 379293d7fbdSSimon Glass goto fail; 380293d7fbdSSimon Glass 381293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 382293d7fbdSSimon Glass bgd[bg_idx].block_id)) 383293d7fbdSSimon Glass goto fail; 384293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 385293d7fbdSSimon Glass } 386293d7fbdSSimon Glass debug("tigp buffer itself releasing %ld\n", blknr); 387293d7fbdSSimon Glass } 388293d7fbdSSimon Glass fail: 389293d7fbdSSimon Glass free(tib_start_addr); 390293d7fbdSSimon Glass free(tipb_start_addr); 391293d7fbdSSimon Glass free(journal_buffer); 392293d7fbdSSimon Glass } 393293d7fbdSSimon Glass 394293d7fbdSSimon Glass static int ext4fs_delete_file(int inodeno) 395293d7fbdSSimon Glass { 396293d7fbdSSimon Glass struct ext2_inode inode; 397293d7fbdSSimon Glass short status; 398293d7fbdSSimon Glass int i; 399293d7fbdSSimon Glass int remainder; 400293d7fbdSSimon Glass long int blknr; 401293d7fbdSSimon Glass int bg_idx; 402293d7fbdSSimon Glass int ibmap_idx; 403293d7fbdSSimon Glass char *read_buffer = NULL; 404293d7fbdSSimon Glass char *start_block_address = NULL; 405293d7fbdSSimon Glass unsigned int no_blocks; 406293d7fbdSSimon Glass 407293d7fbdSSimon Glass static int prev_bg_bmap_idx = -1; 408293d7fbdSSimon Glass unsigned int inodes_per_block; 409293d7fbdSSimon Glass long int blkno; 410293d7fbdSSimon Glass unsigned int blkoff; 411293d7fbdSSimon Glass unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; 412293d7fbdSSimon Glass unsigned int inode_per_grp = ext4fs_root->sblock.inodes_per_group; 413293d7fbdSSimon Glass struct ext2_inode *inode_buffer = NULL; 414293d7fbdSSimon Glass struct ext2_block_group *bgd = NULL; 415293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 416293d7fbdSSimon Glass char *journal_buffer = zalloc(fs->blksz); 417293d7fbdSSimon Glass if (!journal_buffer) 418293d7fbdSSimon Glass return -ENOMEM; 419293d7fbdSSimon Glass /* get the block group descriptor table */ 420293d7fbdSSimon Glass bgd = (struct ext2_block_group *)fs->gdtable; 421293d7fbdSSimon Glass status = ext4fs_read_inode(ext4fs_root, inodeno, &inode); 422293d7fbdSSimon Glass if (status == 0) 423293d7fbdSSimon Glass goto fail; 424293d7fbdSSimon Glass 425293d7fbdSSimon Glass /* read the block no allocated to a file */ 426293d7fbdSSimon Glass no_blocks = inode.size / fs->blksz; 427293d7fbdSSimon Glass if (inode.size % fs->blksz) 428293d7fbdSSimon Glass no_blocks++; 429293d7fbdSSimon Glass 430293d7fbdSSimon Glass if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) { 431293d7fbdSSimon Glass struct ext2fs_node *node_inode = 432293d7fbdSSimon Glass zalloc(sizeof(struct ext2fs_node)); 433293d7fbdSSimon Glass if (!node_inode) 434293d7fbdSSimon Glass goto fail; 435293d7fbdSSimon Glass node_inode->data = ext4fs_root; 436293d7fbdSSimon Glass node_inode->ino = inodeno; 437293d7fbdSSimon Glass node_inode->inode_read = 0; 438293d7fbdSSimon Glass memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode)); 439293d7fbdSSimon Glass 440293d7fbdSSimon Glass for (i = 0; i < no_blocks; i++) { 441293d7fbdSSimon Glass blknr = read_allocated_block(&(node_inode->inode), i); 442293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 44335dd055bSŁukasz Majewski if (fs->blksz == 1024) { 444293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 445293d7fbdSSimon Glass if (!remainder) 446293d7fbdSSimon Glass bg_idx--; 447293d7fbdSSimon Glass } 448293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], 449293d7fbdSSimon Glass bg_idx); 450293d7fbdSSimon Glass debug("EXT4_EXTENTS Block releasing %ld: %d\n", 451293d7fbdSSimon Glass blknr, bg_idx); 452293d7fbdSSimon Glass 453293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 454293d7fbdSSimon Glass fs->sb->free_blocks++; 455293d7fbdSSimon Glass 456293d7fbdSSimon Glass /* journal backup */ 457293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 458293d7fbdSSimon Glass status = 45904735e9cSFrederic Leroy ext4fs_devread((lbaint_t) 46004735e9cSFrederic Leroy bgd[bg_idx].block_id * 461293d7fbdSSimon Glass fs->sect_perblk, 0, 462293d7fbdSSimon Glass fs->blksz, journal_buffer); 463293d7fbdSSimon Glass if (status == 0) 464293d7fbdSSimon Glass goto fail; 465293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 466293d7fbdSSimon Glass bgd[bg_idx].block_id)) 467293d7fbdSSimon Glass goto fail; 468293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 469293d7fbdSSimon Glass } 470293d7fbdSSimon Glass } 471293d7fbdSSimon Glass if (node_inode) { 472293d7fbdSSimon Glass free(node_inode); 473293d7fbdSSimon Glass node_inode = NULL; 474293d7fbdSSimon Glass } 475293d7fbdSSimon Glass } else { 476293d7fbdSSimon Glass 477293d7fbdSSimon Glass delete_single_indirect_block(&inode); 478293d7fbdSSimon Glass delete_double_indirect_block(&inode); 479293d7fbdSSimon Glass delete_triple_indirect_block(&inode); 480293d7fbdSSimon Glass 481293d7fbdSSimon Glass /* read the block no allocated to a file */ 482293d7fbdSSimon Glass no_blocks = inode.size / fs->blksz; 483293d7fbdSSimon Glass if (inode.size % fs->blksz) 484293d7fbdSSimon Glass no_blocks++; 485293d7fbdSSimon Glass for (i = 0; i < no_blocks; i++) { 486293d7fbdSSimon Glass blknr = read_allocated_block(&inode, i); 487293d7fbdSSimon Glass bg_idx = blknr / blk_per_grp; 48835dd055bSŁukasz Majewski if (fs->blksz == 1024) { 489293d7fbdSSimon Glass remainder = blknr % blk_per_grp; 490293d7fbdSSimon Glass if (!remainder) 491293d7fbdSSimon Glass bg_idx--; 492293d7fbdSSimon Glass } 493293d7fbdSSimon Glass ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], 494293d7fbdSSimon Glass bg_idx); 495293d7fbdSSimon Glass debug("ActualB releasing %ld: %d\n", blknr, bg_idx); 496293d7fbdSSimon Glass 497293d7fbdSSimon Glass bgd[bg_idx].free_blocks++; 498293d7fbdSSimon Glass fs->sb->free_blocks++; 499293d7fbdSSimon Glass /* journal backup */ 500293d7fbdSSimon Glass if (prev_bg_bmap_idx != bg_idx) { 501293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 50204735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t) 50304735e9cSFrederic Leroy bgd[bg_idx].block_id 504293d7fbdSSimon Glass * fs->sect_perblk, 505293d7fbdSSimon Glass 0, fs->blksz, 506293d7fbdSSimon Glass journal_buffer); 507293d7fbdSSimon Glass if (status == 0) 508293d7fbdSSimon Glass goto fail; 509293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, 510293d7fbdSSimon Glass bgd[bg_idx].block_id)) 511293d7fbdSSimon Glass goto fail; 512293d7fbdSSimon Glass prev_bg_bmap_idx = bg_idx; 513293d7fbdSSimon Glass } 514293d7fbdSSimon Glass } 515293d7fbdSSimon Glass } 516293d7fbdSSimon Glass 517293d7fbdSSimon Glass /* from the inode no to blockno */ 518293d7fbdSSimon Glass inodes_per_block = fs->blksz / fs->inodesz; 519293d7fbdSSimon Glass ibmap_idx = inodeno / inode_per_grp; 520293d7fbdSSimon Glass 521293d7fbdSSimon Glass /* get the block no */ 522293d7fbdSSimon Glass inodeno--; 523293d7fbdSSimon Glass blkno = __le32_to_cpu(bgd[ibmap_idx].inode_table_id) + 524293d7fbdSSimon Glass (inodeno % __le32_to_cpu(inode_per_grp)) / inodes_per_block; 525293d7fbdSSimon Glass 526293d7fbdSSimon Glass /* get the offset of the inode */ 527293d7fbdSSimon Glass blkoff = ((inodeno) % inodes_per_block) * fs->inodesz; 528293d7fbdSSimon Glass 529293d7fbdSSimon Glass /* read the block no containing the inode */ 530293d7fbdSSimon Glass read_buffer = zalloc(fs->blksz); 531293d7fbdSSimon Glass if (!read_buffer) 532293d7fbdSSimon Glass goto fail; 533293d7fbdSSimon Glass start_block_address = read_buffer; 53404735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)blkno * fs->sect_perblk, 535293d7fbdSSimon Glass 0, fs->blksz, read_buffer); 536293d7fbdSSimon Glass if (status == 0) 537293d7fbdSSimon Glass goto fail; 538293d7fbdSSimon Glass 539293d7fbdSSimon Glass if (ext4fs_log_journal(read_buffer, blkno)) 540293d7fbdSSimon Glass goto fail; 541293d7fbdSSimon Glass 542293d7fbdSSimon Glass read_buffer = read_buffer + blkoff; 543293d7fbdSSimon Glass inode_buffer = (struct ext2_inode *)read_buffer; 544293d7fbdSSimon Glass memset(inode_buffer, '\0', sizeof(struct ext2_inode)); 545293d7fbdSSimon Glass 546293d7fbdSSimon Glass /* write the inode to original position in inode table */ 547293d7fbdSSimon Glass if (ext4fs_put_metadata(start_block_address, blkno)) 548293d7fbdSSimon Glass goto fail; 549293d7fbdSSimon Glass 550293d7fbdSSimon Glass /* update the respective inode bitmaps */ 551293d7fbdSSimon Glass inodeno++; 552293d7fbdSSimon Glass ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx); 553293d7fbdSSimon Glass bgd[ibmap_idx].free_inodes++; 554293d7fbdSSimon Glass fs->sb->free_inodes++; 555293d7fbdSSimon Glass /* journal backup */ 556293d7fbdSSimon Glass memset(journal_buffer, '\0', fs->blksz); 55704735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)bgd[ibmap_idx].inode_id * 558293d7fbdSSimon Glass fs->sect_perblk, 0, fs->blksz, journal_buffer); 559293d7fbdSSimon Glass if (status == 0) 560293d7fbdSSimon Glass goto fail; 561293d7fbdSSimon Glass if (ext4fs_log_journal(journal_buffer, bgd[ibmap_idx].inode_id)) 562293d7fbdSSimon Glass goto fail; 563293d7fbdSSimon Glass 564293d7fbdSSimon Glass ext4fs_update(); 565293d7fbdSSimon Glass ext4fs_deinit(); 5668b454eeeSŁukasz Majewski ext4fs_reinit_global(); 567293d7fbdSSimon Glass 568293d7fbdSSimon Glass if (ext4fs_init() != 0) { 569293d7fbdSSimon Glass printf("error in File System init\n"); 570293d7fbdSSimon Glass goto fail; 571293d7fbdSSimon Glass } 572293d7fbdSSimon Glass 573293d7fbdSSimon Glass free(start_block_address); 574293d7fbdSSimon Glass free(journal_buffer); 575293d7fbdSSimon Glass 576293d7fbdSSimon Glass return 0; 577293d7fbdSSimon Glass fail: 578293d7fbdSSimon Glass free(start_block_address); 579293d7fbdSSimon Glass free(journal_buffer); 580293d7fbdSSimon Glass 581293d7fbdSSimon Glass return -1; 582293d7fbdSSimon Glass } 583293d7fbdSSimon Glass 584293d7fbdSSimon Glass int ext4fs_init(void) 585293d7fbdSSimon Glass { 586293d7fbdSSimon Glass short status; 587293d7fbdSSimon Glass int i; 588293d7fbdSSimon Glass unsigned int real_free_blocks = 0; 589293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 590293d7fbdSSimon Glass 591293d7fbdSSimon Glass /* populate fs */ 592293d7fbdSSimon Glass fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root); 593293d7fbdSSimon Glass fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root); 59450ce4c07SEgbert Eich fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz; 595293d7fbdSSimon Glass 596293d7fbdSSimon Glass /* get the superblock */ 597293d7fbdSSimon Glass fs->sb = zalloc(SUPERBLOCK_SIZE); 598293d7fbdSSimon Glass if (!fs->sb) 599293d7fbdSSimon Glass return -ENOMEM; 60050ce4c07SEgbert Eich if (!ext4_read_superblock((char *)fs->sb)) 601293d7fbdSSimon Glass goto fail; 602293d7fbdSSimon Glass 603293d7fbdSSimon Glass /* init journal */ 604293d7fbdSSimon Glass if (ext4fs_init_journal()) 605293d7fbdSSimon Glass goto fail; 606293d7fbdSSimon Glass 607293d7fbdSSimon Glass /* get total no of blockgroups */ 608293d7fbdSSimon Glass fs->no_blkgrp = (uint32_t)ext4fs_div_roundup( 609293d7fbdSSimon Glass (ext4fs_root->sblock.total_blocks - 610293d7fbdSSimon Glass ext4fs_root->sblock.first_data_block), 611293d7fbdSSimon Glass ext4fs_root->sblock.blocks_per_group); 612293d7fbdSSimon Glass 613293d7fbdSSimon Glass /* get the block group descriptor table */ 614293d7fbdSSimon Glass fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1); 615293d7fbdSSimon Glass if (ext4fs_get_bgdtable() == -1) { 616293d7fbdSSimon Glass printf("Error in getting the block group descriptor table\n"); 617293d7fbdSSimon Glass goto fail; 618293d7fbdSSimon Glass } 619293d7fbdSSimon Glass fs->bgd = (struct ext2_block_group *)fs->gdtable; 620293d7fbdSSimon Glass 621293d7fbdSSimon Glass /* load all the available bitmap block of the partition */ 622293d7fbdSSimon Glass fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *)); 623293d7fbdSSimon Glass if (!fs->blk_bmaps) 624293d7fbdSSimon Glass goto fail; 625293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 626293d7fbdSSimon Glass fs->blk_bmaps[i] = zalloc(fs->blksz); 627293d7fbdSSimon Glass if (!fs->blk_bmaps[i]) 628293d7fbdSSimon Glass goto fail; 629293d7fbdSSimon Glass } 630293d7fbdSSimon Glass 631293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 632293d7fbdSSimon Glass status = 63304735e9cSFrederic Leroy ext4fs_devread((lbaint_t)fs->bgd[i].block_id * 63404735e9cSFrederic Leroy fs->sect_perblk, 0, 635293d7fbdSSimon Glass fs->blksz, (char *)fs->blk_bmaps[i]); 636293d7fbdSSimon Glass if (status == 0) 637293d7fbdSSimon Glass goto fail; 638293d7fbdSSimon Glass } 639293d7fbdSSimon Glass 640293d7fbdSSimon Glass /* load all the available inode bitmap of the partition */ 641293d7fbdSSimon Glass fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *)); 642293d7fbdSSimon Glass if (!fs->inode_bmaps) 643293d7fbdSSimon Glass goto fail; 644293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 645293d7fbdSSimon Glass fs->inode_bmaps[i] = zalloc(fs->blksz); 646293d7fbdSSimon Glass if (!fs->inode_bmaps[i]) 647293d7fbdSSimon Glass goto fail; 648293d7fbdSSimon Glass } 649293d7fbdSSimon Glass 650293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 65104735e9cSFrederic Leroy status = ext4fs_devread((lbaint_t)fs->bgd[i].inode_id * 65204735e9cSFrederic Leroy fs->sect_perblk, 653293d7fbdSSimon Glass 0, fs->blksz, 654293d7fbdSSimon Glass (char *)fs->inode_bmaps[i]); 655293d7fbdSSimon Glass if (status == 0) 656293d7fbdSSimon Glass goto fail; 657293d7fbdSSimon Glass } 658293d7fbdSSimon Glass 659293d7fbdSSimon Glass /* 660293d7fbdSSimon Glass * check filesystem consistency with free blocks of file system 661293d7fbdSSimon Glass * some time we observed that superblock freeblocks does not match 662293d7fbdSSimon Glass * with the blockgroups freeblocks when improper 663293d7fbdSSimon Glass * reboot of a linux kernel 664293d7fbdSSimon Glass */ 665293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) 666293d7fbdSSimon Glass real_free_blocks = real_free_blocks + fs->bgd[i].free_blocks; 667293d7fbdSSimon Glass if (real_free_blocks != fs->sb->free_blocks) 668293d7fbdSSimon Glass fs->sb->free_blocks = real_free_blocks; 669293d7fbdSSimon Glass 670293d7fbdSSimon Glass return 0; 671293d7fbdSSimon Glass fail: 672293d7fbdSSimon Glass ext4fs_deinit(); 673293d7fbdSSimon Glass 674293d7fbdSSimon Glass return -1; 675293d7fbdSSimon Glass } 676293d7fbdSSimon Glass 677293d7fbdSSimon Glass void ext4fs_deinit(void) 678293d7fbdSSimon Glass { 679293d7fbdSSimon Glass int i; 680293d7fbdSSimon Glass struct ext2_inode inode_journal; 681293d7fbdSSimon Glass struct journal_superblock_t *jsb; 682293d7fbdSSimon Glass long int blknr; 683293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 684293d7fbdSSimon Glass 685293d7fbdSSimon Glass /* free journal */ 686293d7fbdSSimon Glass char *temp_buff = zalloc(fs->blksz); 687293d7fbdSSimon Glass if (temp_buff) { 688293d7fbdSSimon Glass ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, 689293d7fbdSSimon Glass &inode_journal); 690293d7fbdSSimon Glass blknr = read_allocated_block(&inode_journal, 691293d7fbdSSimon Glass EXT2_JOURNAL_SUPERBLOCK); 69204735e9cSFrederic Leroy ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz, 693293d7fbdSSimon Glass temp_buff); 694293d7fbdSSimon Glass jsb = (struct journal_superblock_t *)temp_buff; 695293d7fbdSSimon Glass jsb->s_start = cpu_to_be32(0); 6960550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), 697293d7fbdSSimon Glass (struct journal_superblock_t *)temp_buff, fs->blksz); 698293d7fbdSSimon Glass free(temp_buff); 699293d7fbdSSimon Glass } 700293d7fbdSSimon Glass ext4fs_free_journal(); 701293d7fbdSSimon Glass 702293d7fbdSSimon Glass /* get the superblock */ 70350ce4c07SEgbert Eich ext4_read_superblock((char *)fs->sb); 704293d7fbdSSimon Glass fs->sb->feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; 705293d7fbdSSimon Glass put_ext4((uint64_t)(SUPERBLOCK_SIZE), 706293d7fbdSSimon Glass (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); 707293d7fbdSSimon Glass free(fs->sb); 708293d7fbdSSimon Glass fs->sb = NULL; 709293d7fbdSSimon Glass 710293d7fbdSSimon Glass if (fs->blk_bmaps) { 711293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 712293d7fbdSSimon Glass free(fs->blk_bmaps[i]); 713293d7fbdSSimon Glass fs->blk_bmaps[i] = NULL; 714293d7fbdSSimon Glass } 715293d7fbdSSimon Glass free(fs->blk_bmaps); 716293d7fbdSSimon Glass fs->blk_bmaps = NULL; 717293d7fbdSSimon Glass } 718293d7fbdSSimon Glass 719293d7fbdSSimon Glass if (fs->inode_bmaps) { 720293d7fbdSSimon Glass for (i = 0; i < fs->no_blkgrp; i++) { 721293d7fbdSSimon Glass free(fs->inode_bmaps[i]); 722293d7fbdSSimon Glass fs->inode_bmaps[i] = NULL; 723293d7fbdSSimon Glass } 724293d7fbdSSimon Glass free(fs->inode_bmaps); 725293d7fbdSSimon Glass fs->inode_bmaps = NULL; 726293d7fbdSSimon Glass } 727293d7fbdSSimon Glass 728293d7fbdSSimon Glass 729293d7fbdSSimon Glass free(fs->gdtable); 730293d7fbdSSimon Glass fs->gdtable = NULL; 731293d7fbdSSimon Glass fs->bgd = NULL; 732293d7fbdSSimon Glass /* 733293d7fbdSSimon Glass * reinitiliazed the global inode and 734293d7fbdSSimon Glass * block bitmap first execution check variables 735293d7fbdSSimon Glass */ 736293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 737293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 738293d7fbdSSimon Glass fs->curr_inode_no = 0; 739293d7fbdSSimon Glass fs->curr_blkno = 0; 740293d7fbdSSimon Glass } 741293d7fbdSSimon Glass 742293d7fbdSSimon Glass static int ext4fs_write_file(struct ext2_inode *file_inode, 743293d7fbdSSimon Glass int pos, unsigned int len, char *buf) 744293d7fbdSSimon Glass { 745293d7fbdSSimon Glass int i; 746293d7fbdSSimon Glass int blockcnt; 747293d7fbdSSimon Glass unsigned int filesize = __le32_to_cpu(file_inode->size); 748293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 74950ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz; 75050ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(ext4fs_root) - log2blksz; 751293d7fbdSSimon Glass int previous_block_number = -1; 752293d7fbdSSimon Glass int delayed_start = 0; 753293d7fbdSSimon Glass int delayed_extent = 0; 754293d7fbdSSimon Glass int delayed_next = 0; 755293d7fbdSSimon Glass char *delayed_buf = NULL; 756293d7fbdSSimon Glass 757293d7fbdSSimon Glass /* Adjust len so it we can't read past the end of the file. */ 758293d7fbdSSimon Glass if (len > filesize) 759293d7fbdSSimon Glass len = filesize; 760293d7fbdSSimon Glass 761293d7fbdSSimon Glass blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz; 762293d7fbdSSimon Glass 763293d7fbdSSimon Glass for (i = pos / fs->blksz; i < blockcnt; i++) { 764293d7fbdSSimon Glass long int blknr; 765293d7fbdSSimon Glass int blockend = fs->blksz; 766293d7fbdSSimon Glass int skipfirst = 0; 767293d7fbdSSimon Glass blknr = read_allocated_block(file_inode, i); 768293d7fbdSSimon Glass if (blknr < 0) 769293d7fbdSSimon Glass return -1; 770293d7fbdSSimon Glass 77150ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize; 772293d7fbdSSimon Glass 773293d7fbdSSimon Glass if (blknr) { 774293d7fbdSSimon Glass if (previous_block_number != -1) { 775293d7fbdSSimon Glass if (delayed_next == blknr) { 776293d7fbdSSimon Glass delayed_extent += blockend; 77750ce4c07SEgbert Eich delayed_next += blockend >> log2blksz; 778293d7fbdSSimon Glass } else { /* spill */ 77950ce4c07SEgbert Eich put_ext4((uint64_t) 7800550870bSMa Haijun ((uint64_t)delayed_start << log2blksz), 781293d7fbdSSimon Glass delayed_buf, 782293d7fbdSSimon Glass (uint32_t) delayed_extent); 783293d7fbdSSimon Glass previous_block_number = blknr; 784293d7fbdSSimon Glass delayed_start = blknr; 785293d7fbdSSimon Glass delayed_extent = blockend; 786293d7fbdSSimon Glass delayed_buf = buf; 787293d7fbdSSimon Glass delayed_next = blknr + 78850ce4c07SEgbert Eich (blockend >> log2blksz); 789293d7fbdSSimon Glass } 790293d7fbdSSimon Glass } else { 791293d7fbdSSimon Glass previous_block_number = blknr; 792293d7fbdSSimon Glass delayed_start = blknr; 793293d7fbdSSimon Glass delayed_extent = blockend; 794293d7fbdSSimon Glass delayed_buf = buf; 795293d7fbdSSimon Glass delayed_next = blknr + 79650ce4c07SEgbert Eich (blockend >> log2blksz); 797293d7fbdSSimon Glass } 798293d7fbdSSimon Glass } else { 799293d7fbdSSimon Glass if (previous_block_number != -1) { 800293d7fbdSSimon Glass /* spill */ 8010550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)delayed_start << 80250ce4c07SEgbert Eich log2blksz), 80350ce4c07SEgbert Eich delayed_buf, 804293d7fbdSSimon Glass (uint32_t) delayed_extent); 805293d7fbdSSimon Glass previous_block_number = -1; 806293d7fbdSSimon Glass } 807293d7fbdSSimon Glass memset(buf, 0, fs->blksz - skipfirst); 808293d7fbdSSimon Glass } 809293d7fbdSSimon Glass buf += fs->blksz - skipfirst; 810293d7fbdSSimon Glass } 811293d7fbdSSimon Glass if (previous_block_number != -1) { 812293d7fbdSSimon Glass /* spill */ 8130550870bSMa Haijun put_ext4((uint64_t) ((uint64_t)delayed_start << log2blksz), 814293d7fbdSSimon Glass delayed_buf, (uint32_t) delayed_extent); 815293d7fbdSSimon Glass previous_block_number = -1; 816293d7fbdSSimon Glass } 817293d7fbdSSimon Glass 818293d7fbdSSimon Glass return len; 819293d7fbdSSimon Glass } 820293d7fbdSSimon Glass 821293d7fbdSSimon Glass int ext4fs_write(const char *fname, unsigned char *buffer, 822293d7fbdSSimon Glass unsigned long sizebytes) 823293d7fbdSSimon Glass { 824293d7fbdSSimon Glass int ret = 0; 825293d7fbdSSimon Glass struct ext2_inode *file_inode = NULL; 826293d7fbdSSimon Glass unsigned char *inode_buffer = NULL; 827293d7fbdSSimon Glass int parent_inodeno; 828293d7fbdSSimon Glass int inodeno; 829293d7fbdSSimon Glass time_t timestamp = 0; 830293d7fbdSSimon Glass 831293d7fbdSSimon Glass uint64_t bytes_reqd_for_file; 832293d7fbdSSimon Glass unsigned int blks_reqd_for_file; 833293d7fbdSSimon Glass unsigned int blocks_remaining; 834293d7fbdSSimon Glass int existing_file_inodeno; 835293d7fbdSSimon Glass char *temp_ptr = NULL; 836293d7fbdSSimon Glass long int itable_blkno; 837293d7fbdSSimon Glass long int parent_itable_blkno; 838293d7fbdSSimon Glass long int blkoff; 839293d7fbdSSimon Glass struct ext2_sblock *sblock = &(ext4fs_root->sblock); 840293d7fbdSSimon Glass unsigned int inodes_per_block; 841293d7fbdSSimon Glass unsigned int ibmap_idx; 842293d7fbdSSimon Glass struct ext_filesystem *fs = get_fs(); 843293d7fbdSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256); 84446a5707dSJeroen Hofstee memset(filename, 0x00, 256); 845293d7fbdSSimon Glass 846293d7fbdSSimon Glass g_parent_inode = zalloc(sizeof(struct ext2_inode)); 847293d7fbdSSimon Glass if (!g_parent_inode) 848293d7fbdSSimon Glass goto fail; 849293d7fbdSSimon Glass 850293d7fbdSSimon Glass if (ext4fs_init() != 0) { 851293d7fbdSSimon Glass printf("error in File System init\n"); 852293d7fbdSSimon Glass return -1; 853293d7fbdSSimon Glass } 854293d7fbdSSimon Glass inodes_per_block = fs->blksz / fs->inodesz; 855293d7fbdSSimon Glass parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE); 856293d7fbdSSimon Glass if (parent_inodeno == -1) 857293d7fbdSSimon Glass goto fail; 858293d7fbdSSimon Glass if (ext4fs_iget(parent_inodeno, g_parent_inode)) 859293d7fbdSSimon Glass goto fail; 860293d7fbdSSimon Glass /* check if the filename is already present in root */ 861293d7fbdSSimon Glass existing_file_inodeno = ext4fs_filename_check(filename); 862293d7fbdSSimon Glass if (existing_file_inodeno != -1) { 863293d7fbdSSimon Glass ret = ext4fs_delete_file(existing_file_inodeno); 864293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 865293d7fbdSSimon Glass fs->curr_blkno = 0; 866293d7fbdSSimon Glass 867293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 868293d7fbdSSimon Glass fs->curr_inode_no = 0; 869293d7fbdSSimon Glass if (ret) 870293d7fbdSSimon Glass goto fail; 871293d7fbdSSimon Glass } 872293d7fbdSSimon Glass /* calucalate how many blocks required */ 873293d7fbdSSimon Glass bytes_reqd_for_file = sizebytes; 874293d7fbdSSimon Glass blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz); 875293d7fbdSSimon Glass if (do_div(bytes_reqd_for_file, fs->blksz) != 0) { 876293d7fbdSSimon Glass blks_reqd_for_file++; 877293d7fbdSSimon Glass debug("total bytes for a file %u\n", blks_reqd_for_file); 878293d7fbdSSimon Glass } 879293d7fbdSSimon Glass blocks_remaining = blks_reqd_for_file; 880293d7fbdSSimon Glass /* test for available space in partition */ 881293d7fbdSSimon Glass if (fs->sb->free_blocks < blks_reqd_for_file) { 882293d7fbdSSimon Glass printf("Not enough space on partition !!!\n"); 883293d7fbdSSimon Glass goto fail; 884293d7fbdSSimon Glass } 885293d7fbdSSimon Glass 886293d7fbdSSimon Glass ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG); 887293d7fbdSSimon Glass /* prepare file inode */ 888293d7fbdSSimon Glass inode_buffer = zalloc(fs->inodesz); 889293d7fbdSSimon Glass if (!inode_buffer) 890293d7fbdSSimon Glass goto fail; 891293d7fbdSSimon Glass file_inode = (struct ext2_inode *)inode_buffer; 892293d7fbdSSimon Glass file_inode->mode = S_IFREG | S_IRWXU | 893293d7fbdSSimon Glass S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH; 894293d7fbdSSimon Glass /* ToDo: Update correct time */ 895293d7fbdSSimon Glass file_inode->mtime = timestamp; 896293d7fbdSSimon Glass file_inode->atime = timestamp; 897293d7fbdSSimon Glass file_inode->ctime = timestamp; 898293d7fbdSSimon Glass file_inode->nlinks = 1; 899293d7fbdSSimon Glass file_inode->size = sizebytes; 900293d7fbdSSimon Glass 901293d7fbdSSimon Glass /* Allocate data blocks */ 902293d7fbdSSimon Glass ext4fs_allocate_blocks(file_inode, blocks_remaining, 903293d7fbdSSimon Glass &blks_reqd_for_file); 90450ce4c07SEgbert Eich file_inode->blockcnt = (blks_reqd_for_file * fs->blksz) >> 90550ce4c07SEgbert Eich fs->dev_desc->log2blksz; 906293d7fbdSSimon Glass 907293d7fbdSSimon Glass temp_ptr = zalloc(fs->blksz); 908293d7fbdSSimon Glass if (!temp_ptr) 909293d7fbdSSimon Glass goto fail; 910293d7fbdSSimon Glass ibmap_idx = inodeno / ext4fs_root->sblock.inodes_per_group; 911293d7fbdSSimon Glass inodeno--; 912293d7fbdSSimon Glass itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) + 913293d7fbdSSimon Glass (inodeno % __le32_to_cpu(sblock->inodes_per_group)) / 914293d7fbdSSimon Glass inodes_per_block; 915293d7fbdSSimon Glass blkoff = (inodeno % inodes_per_block) * fs->inodesz; 91604735e9cSFrederic Leroy ext4fs_devread((lbaint_t)itable_blkno * fs->sect_perblk, 0, fs->blksz, 91704735e9cSFrederic Leroy temp_ptr); 918293d7fbdSSimon Glass if (ext4fs_log_journal(temp_ptr, itable_blkno)) 919293d7fbdSSimon Glass goto fail; 920293d7fbdSSimon Glass 921293d7fbdSSimon Glass memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz); 922293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, itable_blkno)) 923293d7fbdSSimon Glass goto fail; 924293d7fbdSSimon Glass /* copy the file content into data blocks */ 925293d7fbdSSimon Glass if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) { 926293d7fbdSSimon Glass printf("Error in copying content\n"); 927293d7fbdSSimon Glass goto fail; 928293d7fbdSSimon Glass } 929293d7fbdSSimon Glass ibmap_idx = parent_inodeno / ext4fs_root->sblock.inodes_per_group; 930293d7fbdSSimon Glass parent_inodeno--; 931293d7fbdSSimon Glass parent_itable_blkno = __le32_to_cpu(fs->bgd[ibmap_idx].inode_table_id) + 932293d7fbdSSimon Glass (parent_inodeno % 933293d7fbdSSimon Glass __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; 934293d7fbdSSimon Glass blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz; 935293d7fbdSSimon Glass if (parent_itable_blkno != itable_blkno) { 936293d7fbdSSimon Glass memset(temp_ptr, '\0', fs->blksz); 93704735e9cSFrederic Leroy ext4fs_devread((lbaint_t)parent_itable_blkno * fs->sect_perblk, 938293d7fbdSSimon Glass 0, fs->blksz, temp_ptr); 939293d7fbdSSimon Glass if (ext4fs_log_journal(temp_ptr, parent_itable_blkno)) 940293d7fbdSSimon Glass goto fail; 941293d7fbdSSimon Glass 942293d7fbdSSimon Glass memcpy(temp_ptr + blkoff, g_parent_inode, 943293d7fbdSSimon Glass sizeof(struct ext2_inode)); 944293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno)) 945293d7fbdSSimon Glass goto fail; 946293d7fbdSSimon Glass free(temp_ptr); 947293d7fbdSSimon Glass } else { 948293d7fbdSSimon Glass /* 949293d7fbdSSimon Glass * If parent and child fall in same inode table block 950293d7fbdSSimon Glass * both should be kept in 1 buffer 951293d7fbdSSimon Glass */ 952293d7fbdSSimon Glass memcpy(temp_ptr + blkoff, g_parent_inode, 953293d7fbdSSimon Glass sizeof(struct ext2_inode)); 954293d7fbdSSimon Glass gd_index--; 955293d7fbdSSimon Glass if (ext4fs_put_metadata(temp_ptr, itable_blkno)) 956293d7fbdSSimon Glass goto fail; 957293d7fbdSSimon Glass free(temp_ptr); 958293d7fbdSSimon Glass } 959293d7fbdSSimon Glass ext4fs_update(); 960293d7fbdSSimon Glass ext4fs_deinit(); 961293d7fbdSSimon Glass 962293d7fbdSSimon Glass fs->first_pass_bbmap = 0; 963293d7fbdSSimon Glass fs->curr_blkno = 0; 964293d7fbdSSimon Glass fs->first_pass_ibmap = 0; 965293d7fbdSSimon Glass fs->curr_inode_no = 0; 966293d7fbdSSimon Glass free(inode_buffer); 967293d7fbdSSimon Glass free(g_parent_inode); 968293d7fbdSSimon Glass g_parent_inode = NULL; 969293d7fbdSSimon Glass 970293d7fbdSSimon Glass return 0; 971293d7fbdSSimon Glass fail: 972293d7fbdSSimon Glass ext4fs_deinit(); 973293d7fbdSSimon Glass free(inode_buffer); 974293d7fbdSSimon Glass free(g_parent_inode); 975293d7fbdSSimon Glass g_parent_inode = NULL; 976293d7fbdSSimon Glass 977293d7fbdSSimon Glass return -1; 978293d7fbdSSimon Glass } 9799f12cd0eSSuriyan Ramasami 9809f12cd0eSSuriyan Ramasami int ext4_write_file(const char *filename, void *buf, loff_t offset, 9819f12cd0eSSuriyan Ramasami loff_t len, loff_t *actwrite) 9829f12cd0eSSuriyan Ramasami { 9839f12cd0eSSuriyan Ramasami int ret; 9849f12cd0eSSuriyan Ramasami 9859f12cd0eSSuriyan Ramasami if (offset != 0) { 9869f12cd0eSSuriyan Ramasami printf("** Cannot support non-zero offset **\n"); 9879f12cd0eSSuriyan Ramasami return -1; 9889f12cd0eSSuriyan Ramasami } 9899f12cd0eSSuriyan Ramasami 9909f12cd0eSSuriyan Ramasami /* mount the filesystem */ 9919f12cd0eSSuriyan Ramasami if (!ext4fs_mount(0)) { 9929f12cd0eSSuriyan Ramasami printf("** Error Bad ext4 partition **\n"); 9939f12cd0eSSuriyan Ramasami goto fail; 9949f12cd0eSSuriyan Ramasami } 9959f12cd0eSSuriyan Ramasami 9969f12cd0eSSuriyan Ramasami ret = ext4fs_write(filename, buf, len); 9979f12cd0eSSuriyan Ramasami 9989f12cd0eSSuriyan Ramasami if (ret) { 9999f12cd0eSSuriyan Ramasami printf("** Error ext4fs_write() **\n"); 10009f12cd0eSSuriyan Ramasami goto fail; 10019f12cd0eSSuriyan Ramasami } 10029f12cd0eSSuriyan Ramasami ext4fs_close(); 10039f12cd0eSSuriyan Ramasami 100422b7509eSPrzemyslaw Marczak *actwrite = len; 100522b7509eSPrzemyslaw Marczak 10069f12cd0eSSuriyan Ramasami return 0; 10079f12cd0eSSuriyan Ramasami 10089f12cd0eSSuriyan Ramasami fail: 10099f12cd0eSSuriyan Ramasami ext4fs_close(); 101022b7509eSPrzemyslaw Marczak *actwrite = 0; 10119f12cd0eSSuriyan Ramasami 10129f12cd0eSSuriyan Ramasami return -1; 10139f12cd0eSSuriyan Ramasami } 1014