blob: a2dfff8197928a1e885fb1151938ef6e2b7165df [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass6c8f3aa2012-12-26 09:53:28 +00002/*
3 * (C) Copyright 2011 - 2012 Samsung Electronics
4 * EXT4 filesystem implementation in Uboot by
5 * Uma Shankar <uma.shankar@samsung.com>
6 * Manjunatha C Achar <a.manjunatha@samsung.com>
7 *
8 * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
9 * Ext4 read optimization taken from Open-Moko
10 * Qi bootloader
11 *
12 * (C) Copyright 2004
13 * esd gmbh <www.esd-electronics.com>
14 * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
15 *
16 * based on code from grub2 fs/ext2.c and fs/fshelp.c by
17 * GRUB -- GRand Unified Bootloader
18 * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
19 *
20 * ext4write : Based on generic ext4 protocol.
Simon Glass6c8f3aa2012-12-26 09:53:28 +000021 */
22
Simon Glass655306c2020-05-10 11:39:58 -060023#include <blk.h>
Simon Glass0f2af882020-05-10 11:40:05 -060024#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070025#include <malloc.h>
Simon Glass2dd337a2015-09-02 17:24:58 -060026#include <memalign.h>
Simon Glass655306c2020-05-10 11:39:58 -060027#include <part.h>
Simon Glass6c8f3aa2012-12-26 09:53:28 +000028#include <linux/stat.h>
29#include <div64.h>
30#include "ext4_common.h"
31
Michael Walle13179c22016-09-01 11:21:40 +020032static inline void ext4fs_sb_free_inodes_inc(struct ext2_sblock *sb)
33{
34 sb->free_inodes = cpu_to_le32(le32_to_cpu(sb->free_inodes) + 1);
35}
36
37static inline void ext4fs_sb_free_blocks_inc(struct ext2_sblock *sb)
38{
39 sb->free_blocks = cpu_to_le32(le32_to_cpu(sb->free_blocks) + 1);
40}
41
Stefan Brüns05313d12016-09-20 01:13:01 +020042static inline void ext4fs_bg_free_inodes_inc
43 (struct ext2_block_group *bg, const struct ext_filesystem *fs)
Michael Walle13179c22016-09-01 11:21:40 +020044{
Stefan Brüns05313d12016-09-20 01:13:01 +020045 uint32_t free_inodes = le16_to_cpu(bg->free_inodes);
46 if (fs->gdsize == 64)
47 free_inodes += le16_to_cpu(bg->free_inodes_high) << 16;
48 free_inodes++;
49
50 bg->free_inodes = cpu_to_le16(free_inodes & 0xffff);
51 if (fs->gdsize == 64)
52 bg->free_inodes_high = cpu_to_le16(free_inodes >> 16);
Michael Walle13179c22016-09-01 11:21:40 +020053}
54
Stefan Brüns05313d12016-09-20 01:13:01 +020055static inline void ext4fs_bg_free_blocks_inc
56 (struct ext2_block_group *bg, const struct ext_filesystem *fs)
Michael Walle13179c22016-09-01 11:21:40 +020057{
Stefan Brüns05313d12016-09-20 01:13:01 +020058 uint32_t free_blocks = le16_to_cpu(bg->free_blocks);
59 if (fs->gdsize == 64)
60 free_blocks += le16_to_cpu(bg->free_blocks_high) << 16;
61 free_blocks++;
62
63 bg->free_blocks = cpu_to_le16(free_blocks & 0xffff);
64 if (fs->gdsize == 64)
65 bg->free_blocks_high = cpu_to_le16(free_blocks >> 16);
Michael Walle13179c22016-09-01 11:21:40 +020066}
67
Simon Glass6c8f3aa2012-12-26 09:53:28 +000068static void ext4fs_update(void)
69{
70 short i;
71 ext4fs_update_journal();
72 struct ext_filesystem *fs = get_fs();
Stefan Brünsbad73812016-09-17 02:10:10 +020073 struct ext2_block_group *bgd = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +000074
75 /* update super block */
76 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
77 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
78
Stefan Brünsbad73812016-09-17 02:10:10 +020079 /* update block bitmaps */
Simon Glass6c8f3aa2012-12-26 09:53:28 +000080 for (i = 0; i < fs->no_blkgrp; i++) {
Stefan Brünsbad73812016-09-17 02:10:10 +020081 bgd = ext4fs_get_group_descriptor(fs, i);
82 bgd->bg_checksum = cpu_to_le16(ext4fs_checksum_update(i));
83 uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
84 put_ext4(b_bitmap_blk * fs->blksz,
Simon Glass6c8f3aa2012-12-26 09:53:28 +000085 fs->blk_bmaps[i], fs->blksz);
86 }
87
Stefan Brünsbad73812016-09-17 02:10:10 +020088 /* update inode bitmaps */
Simon Glass6c8f3aa2012-12-26 09:53:28 +000089 for (i = 0; i < fs->no_blkgrp; i++) {
Stefan Brünsbad73812016-09-17 02:10:10 +020090 bgd = ext4fs_get_group_descriptor(fs, i);
91 uint64_t i_bitmap_blk = ext4fs_bg_get_inode_id(bgd, fs);
92 put_ext4(i_bitmap_blk * fs->blksz,
Simon Glass6c8f3aa2012-12-26 09:53:28 +000093 fs->inode_bmaps[i], fs->blksz);
94 }
95
96 /* update the block group descriptor table */
Ma Haijune0996ca2014-01-08 08:15:33 +080097 put_ext4((uint64_t)((uint64_t)fs->gdtable_blkno * (uint64_t)fs->blksz),
Simon Glass6c8f3aa2012-12-26 09:53:28 +000098 (struct ext2_block_group *)fs->gdtable,
99 (fs->blksz * fs->no_blk_pergdt));
100
101 ext4fs_dump_metadata();
102
103 gindex = 0;
104 gd_index = 0;
105}
106
107int ext4fs_get_bgdtable(void)
108{
109 int status;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000110 struct ext_filesystem *fs = get_fs();
Stefan Brünsbad73812016-09-17 02:10:10 +0200111 int gdsize_total = ROUND(fs->no_blkgrp * fs->gdsize, fs->blksz);
112 fs->no_blk_pergdt = gdsize_total / fs->blksz;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000113
114 /* allocate memory for gdtable */
Stefan Brünsbad73812016-09-17 02:10:10 +0200115 fs->gdtable = zalloc(gdsize_total);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000116 if (!fs->gdtable)
117 return -ENOMEM;
118 /* read the group descriptor table */
Frederic Leroye7ee0282013-06-26 18:11:25 +0200119 status = ext4fs_devread((lbaint_t)fs->gdtable_blkno * fs->sect_perblk,
120 0, fs->blksz * fs->no_blk_pergdt, fs->gdtable);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000121 if (status == 0)
122 goto fail;
123
124 if (ext4fs_log_gdt(fs->gdtable)) {
125 printf("Error in ext4fs_log_gdt\n");
126 return -1;
127 }
128
129 return 0;
130fail:
131 free(fs->gdtable);
132 fs->gdtable = NULL;
133
134 return -1;
135}
136
137static void delete_single_indirect_block(struct ext2_inode *inode)
138{
139 struct ext2_block_group *bgd = NULL;
140 static int prev_bg_bmap_idx = -1;
Michael Walle13179c22016-09-01 11:21:40 +0200141 uint32_t blknr;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000142 int remainder;
143 int bg_idx;
144 int status;
Michael Walle13179c22016-09-01 11:21:40 +0200145 uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000146 struct ext_filesystem *fs = get_fs();
147 char *journal_buffer = zalloc(fs->blksz);
148 if (!journal_buffer) {
149 printf("No memory\n");
150 return;
151 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000152
153 /* deleting the single indirect block associated with inode */
154 if (inode->b.blocks.indir_block != 0) {
Michael Walle13179c22016-09-01 11:21:40 +0200155 blknr = le32_to_cpu(inode->b.blocks.indir_block);
156 debug("SIPB releasing %u\n", blknr);
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200157 bg_idx = blknr / blk_per_grp;
158 if (fs->blksz == 1024) {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000159 remainder = blknr % blk_per_grp;
160 if (!remainder)
161 bg_idx--;
162 }
163 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Stefan Brünsbad73812016-09-17 02:10:10 +0200164 /* get block group descriptor table */
165 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200166 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200167 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000168 /* journal backup */
169 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200170 uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200171 status = ext4fs_devread(
Stefan Brünsbad73812016-09-17 02:10:10 +0200172 b_bitmap_blk * fs->sect_perblk,
173 0, fs->blksz, journal_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000174 if (status == 0)
175 goto fail;
Stefan Brünsbad73812016-09-17 02:10:10 +0200176 if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000177 goto fail;
178 prev_bg_bmap_idx = bg_idx;
179 }
180 }
181fail:
182 free(journal_buffer);
183}
184
185static void delete_double_indirect_block(struct ext2_inode *inode)
186{
187 int i;
188 short status;
189 static int prev_bg_bmap_idx = -1;
Michael Walle13179c22016-09-01 11:21:40 +0200190 uint32_t blknr;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000191 int remainder;
192 int bg_idx;
Michael Walle13179c22016-09-01 11:21:40 +0200193 uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
194 __le32 *di_buffer = NULL;
195 void *dib_start_addr = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000196 struct ext2_block_group *bgd = NULL;
197 struct ext_filesystem *fs = get_fs();
198 char *journal_buffer = zalloc(fs->blksz);
199 if (!journal_buffer) {
200 printf("No memory\n");
201 return;
202 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000203
204 if (inode->b.blocks.double_indir_block != 0) {
205 di_buffer = zalloc(fs->blksz);
206 if (!di_buffer) {
207 printf("No memory\n");
208 return;
209 }
Michael Walle13179c22016-09-01 11:21:40 +0200210 dib_start_addr = di_buffer;
211 blknr = le32_to_cpu(inode->b.blocks.double_indir_block);
Frederic Leroye7ee0282013-06-26 18:11:25 +0200212 status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
213 fs->blksz, (char *)di_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000214 for (i = 0; i < fs->blksz / sizeof(int); i++) {
215 if (*di_buffer == 0)
216 break;
217
218 debug("DICB releasing %u\n", *di_buffer);
Michael Walle13179c22016-09-01 11:21:40 +0200219 bg_idx = le32_to_cpu(*di_buffer) / blk_per_grp;
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200220 if (fs->blksz == 1024) {
Michael Walle13179c22016-09-01 11:21:40 +0200221 remainder = le32_to_cpu(*di_buffer) % blk_per_grp;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000222 if (!remainder)
223 bg_idx--;
224 }
Stefan Brünsbad73812016-09-17 02:10:10 +0200225 /* get block group descriptor table */
226 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Michael Walle13179c22016-09-01 11:21:40 +0200227 ext4fs_reset_block_bmap(le32_to_cpu(*di_buffer),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000228 fs->blk_bmaps[bg_idx], bg_idx);
229 di_buffer++;
Stefan Brüns05313d12016-09-20 01:13:01 +0200230 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200231 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000232 /* journal backup */
233 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200234 uint64_t b_bitmap_blk =
235 ext4fs_bg_get_block_id(bgd, fs);
236 status = ext4fs_devread(b_bitmap_blk
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000237 * fs->sect_perblk, 0,
238 fs->blksz,
239 journal_buffer);
240 if (status == 0)
241 goto fail;
242
243 if (ext4fs_log_journal(journal_buffer,
Stefan Brünsbad73812016-09-17 02:10:10 +0200244 b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000245 goto fail;
246 prev_bg_bmap_idx = bg_idx;
247 }
248 }
249
250 /* removing the parent double indirect block */
Michael Walle13179c22016-09-01 11:21:40 +0200251 blknr = le32_to_cpu(inode->b.blocks.double_indir_block);
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200252 bg_idx = blknr / blk_per_grp;
253 if (fs->blksz == 1024) {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000254 remainder = blknr % blk_per_grp;
255 if (!remainder)
256 bg_idx--;
257 }
Stefan Brünsbad73812016-09-17 02:10:10 +0200258 /* get block group descriptor table */
259 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000260 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200261 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200262 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000263 /* journal backup */
264 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200265 uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
266 status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
267 0, fs->blksz, journal_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000268 if (status == 0)
269 goto fail;
270
Stefan Brünsbad73812016-09-17 02:10:10 +0200271 if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000272 goto fail;
273 prev_bg_bmap_idx = bg_idx;
274 }
Michael Walle13179c22016-09-01 11:21:40 +0200275 debug("DIPB releasing %d\n", blknr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000276 }
277fail:
Michael Walle13179c22016-09-01 11:21:40 +0200278 free(dib_start_addr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000279 free(journal_buffer);
280}
281
282static void delete_triple_indirect_block(struct ext2_inode *inode)
283{
284 int i, j;
285 short status;
286 static int prev_bg_bmap_idx = -1;
Michael Walle13179c22016-09-01 11:21:40 +0200287 uint32_t blknr;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000288 int remainder;
289 int bg_idx;
Michael Walle13179c22016-09-01 11:21:40 +0200290 uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
291 __le32 *tigp_buffer = NULL;
292 void *tib_start_addr = NULL;
293 __le32 *tip_buffer = NULL;
294 void *tipb_start_addr = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000295 struct ext2_block_group *bgd = NULL;
296 struct ext_filesystem *fs = get_fs();
297 char *journal_buffer = zalloc(fs->blksz);
298 if (!journal_buffer) {
299 printf("No memory\n");
300 return;
301 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000302
303 if (inode->b.blocks.triple_indir_block != 0) {
304 tigp_buffer = zalloc(fs->blksz);
305 if (!tigp_buffer) {
306 printf("No memory\n");
307 return;
308 }
Michael Walle13179c22016-09-01 11:21:40 +0200309 tib_start_addr = tigp_buffer;
310 blknr = le32_to_cpu(inode->b.blocks.triple_indir_block);
Frederic Leroye7ee0282013-06-26 18:11:25 +0200311 status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
312 fs->blksz, (char *)tigp_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000313 for (i = 0; i < fs->blksz / sizeof(int); i++) {
314 if (*tigp_buffer == 0)
315 break;
316 debug("tigp buffer releasing %u\n", *tigp_buffer);
317
318 tip_buffer = zalloc(fs->blksz);
319 if (!tip_buffer)
320 goto fail;
Michael Walle13179c22016-09-01 11:21:40 +0200321 tipb_start_addr = tip_buffer;
322 status = ext4fs_devread((lbaint_t)le32_to_cpu(*tigp_buffer) *
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000323 fs->sect_perblk, 0, fs->blksz,
324 (char *)tip_buffer);
325 for (j = 0; j < fs->blksz / sizeof(int); j++) {
Michael Walle13179c22016-09-01 11:21:40 +0200326 if (le32_to_cpu(*tip_buffer) == 0)
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000327 break;
Michael Walle13179c22016-09-01 11:21:40 +0200328 bg_idx = le32_to_cpu(*tip_buffer) / blk_per_grp;
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200329 if (fs->blksz == 1024) {
Michael Walle13179c22016-09-01 11:21:40 +0200330 remainder = le32_to_cpu(*tip_buffer) % blk_per_grp;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000331 if (!remainder)
332 bg_idx--;
333 }
334
Michael Walle13179c22016-09-01 11:21:40 +0200335 ext4fs_reset_block_bmap(le32_to_cpu(*tip_buffer),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000336 fs->blk_bmaps[bg_idx],
337 bg_idx);
338
339 tip_buffer++;
Stefan Brünsbad73812016-09-17 02:10:10 +0200340 /* get block group descriptor table */
341 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200342 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200343 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000344 /* journal backup */
345 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200346 uint64_t b_bitmap_blk =
347 ext4fs_bg_get_block_id(bgd, fs);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000348 status =
349 ext4fs_devread(
Stefan Brünsbad73812016-09-17 02:10:10 +0200350 b_bitmap_blk *
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000351 fs->sect_perblk, 0,
352 fs->blksz,
353 journal_buffer);
354 if (status == 0)
355 goto fail;
356
357 if (ext4fs_log_journal(journal_buffer,
Stefan Brünsbad73812016-09-17 02:10:10 +0200358 b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000359 goto fail;
360 prev_bg_bmap_idx = bg_idx;
361 }
362 }
363 free(tipb_start_addr);
364 tipb_start_addr = NULL;
365
366 /*
367 * removing the grand parent blocks
368 * which is connected to inode
369 */
Michael Walle13179c22016-09-01 11:21:40 +0200370 bg_idx = le32_to_cpu(*tigp_buffer) / blk_per_grp;
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200371 if (fs->blksz == 1024) {
Michael Walle13179c22016-09-01 11:21:40 +0200372 remainder = le32_to_cpu(*tigp_buffer) % blk_per_grp;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000373 if (!remainder)
374 bg_idx--;
375 }
Michael Walle13179c22016-09-01 11:21:40 +0200376 ext4fs_reset_block_bmap(le32_to_cpu(*tigp_buffer),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000377 fs->blk_bmaps[bg_idx], bg_idx);
378
379 tigp_buffer++;
Stefan Brünsbad73812016-09-17 02:10:10 +0200380 /* get block group descriptor table */
381 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200382 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200383 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000384 /* journal backup */
385 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200386 uint64_t b_bitmap_blk =
387 ext4fs_bg_get_block_id(bgd, fs);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000388 memset(journal_buffer, '\0', fs->blksz);
Stefan Brünsbad73812016-09-17 02:10:10 +0200389 status = ext4fs_devread(b_bitmap_blk *
390 fs->sect_perblk, 0,
391 fs->blksz,
392 journal_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000393 if (status == 0)
394 goto fail;
395
396 if (ext4fs_log_journal(journal_buffer,
Stefan Brünsbad73812016-09-17 02:10:10 +0200397 b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000398 goto fail;
399 prev_bg_bmap_idx = bg_idx;
400 }
401 }
402
403 /* removing the grand parent triple indirect block */
Michael Walle13179c22016-09-01 11:21:40 +0200404 blknr = le32_to_cpu(inode->b.blocks.triple_indir_block);
Łukasz Majewski5767dc82014-05-06 09:36:04 +0200405 bg_idx = blknr / blk_per_grp;
406 if (fs->blksz == 1024) {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000407 remainder = blknr % blk_per_grp;
408 if (!remainder)
409 bg_idx--;
410 }
411 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
Stefan Brünsbad73812016-09-17 02:10:10 +0200412 /* get block group descriptor table */
413 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200414 ext4fs_bg_free_blocks_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200415 ext4fs_sb_free_blocks_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000416 /* journal backup */
417 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200418 uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
419 status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
420 0, fs->blksz, journal_buffer);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000421 if (status == 0)
422 goto fail;
423
Stefan Brünsbad73812016-09-17 02:10:10 +0200424 if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000425 goto fail;
426 prev_bg_bmap_idx = bg_idx;
427 }
Michael Walle13179c22016-09-01 11:21:40 +0200428 debug("tigp buffer itself releasing %d\n", blknr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000429 }
430fail:
431 free(tib_start_addr);
432 free(tipb_start_addr);
433 free(journal_buffer);
434}
435
436static int ext4fs_delete_file(int inodeno)
437{
438 struct ext2_inode inode;
439 short status;
440 int i;
441 int remainder;
442 long int blknr;
443 int bg_idx;
444 int ibmap_idx;
445 char *read_buffer = NULL;
446 char *start_block_address = NULL;
Michael Walle13179c22016-09-01 11:21:40 +0200447 uint32_t no_blocks;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000448
449 static int prev_bg_bmap_idx = -1;
450 unsigned int inodes_per_block;
Michael Walle13179c22016-09-01 11:21:40 +0200451 uint32_t blkno;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000452 unsigned int blkoff;
Michael Walle13179c22016-09-01 11:21:40 +0200453 uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
454 uint32_t inode_per_grp = le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000455 struct ext2_inode *inode_buffer = NULL;
456 struct ext2_block_group *bgd = NULL;
457 struct ext_filesystem *fs = get_fs();
458 char *journal_buffer = zalloc(fs->blksz);
459 if (!journal_buffer)
460 return -ENOMEM;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000461 status = ext4fs_read_inode(ext4fs_root, inodeno, &inode);
462 if (status == 0)
463 goto fail;
464
465 /* read the block no allocated to a file */
Michael Walle13179c22016-09-01 11:21:40 +0200466 no_blocks = le32_to_cpu(inode.size) / fs->blksz;
467 if (le32_to_cpu(inode.size) % fs->blksz)
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000468 no_blocks++;
469
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100470 /*
471 * special case for symlinks whose target are small enough that
472 *it fits in struct ext2_inode.b.symlink: no block had been allocated
473 */
Corentin GUILLEVICd4620ae2023-03-17 13:15:12 +0100474 if (S_ISLNK(le16_to_cpu(inode.mode)) &&
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100475 le32_to_cpu(inode.size) <= sizeof(inode.b.symlink)) {
476 no_blocks = 0;
477 }
478
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000479 if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
Stefan Brüns03527132016-09-06 04:36:54 +0200480 /* FIXME delete extent index blocks, i.e. eh_depth >= 1 */
481 struct ext4_extent_header *eh =
482 (struct ext4_extent_header *)
483 inode.b.blocks.dir_blocks;
484 debug("del: dep=%d entries=%d\n", eh->eh_depth, eh->eh_entries);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000485 } else {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000486 delete_single_indirect_block(&inode);
487 delete_double_indirect_block(&inode);
488 delete_triple_indirect_block(&inode);
Stefan Brüns03527132016-09-06 04:36:54 +0200489 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000490
Stefan Brüns03527132016-09-06 04:36:54 +0200491 /* release data blocks */
492 for (i = 0; i < no_blocks; i++) {
Stephen Warren02d6ca72019-01-30 12:58:05 -0700493 blknr = read_allocated_block(&inode, i, NULL);
Stefan Brüns74674ed2016-09-06 04:36:55 +0200494 if (blknr == 0)
495 continue;
496 if (blknr < 0)
497 goto fail;
Stefan Brüns03527132016-09-06 04:36:54 +0200498 bg_idx = blknr / blk_per_grp;
499 if (fs->blksz == 1024) {
500 remainder = blknr % blk_per_grp;
501 if (!remainder)
502 bg_idx--;
503 }
504 ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
505 bg_idx);
506 debug("EXT4 Block releasing %ld: %d\n", blknr, bg_idx);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000507
Stefan Brünsbad73812016-09-17 02:10:10 +0200508 /* get block group descriptor table */
509 bgd = ext4fs_get_group_descriptor(fs, bg_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200510 ext4fs_bg_free_blocks_inc(bgd, fs);
Stefan Brüns03527132016-09-06 04:36:54 +0200511 ext4fs_sb_free_blocks_inc(fs->sb);
512 /* journal backup */
513 if (prev_bg_bmap_idx != bg_idx) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200514 uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
515 status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
Stefan Brüns03527132016-09-06 04:36:54 +0200516 0, fs->blksz,
517 journal_buffer);
518 if (status == 0)
519 goto fail;
Stefan Brünsbad73812016-09-17 02:10:10 +0200520 if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
Stefan Brüns03527132016-09-06 04:36:54 +0200521 goto fail;
522 prev_bg_bmap_idx = bg_idx;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000523 }
524 }
525
Stefan Brüns03527132016-09-06 04:36:54 +0200526 /* release inode */
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000527 /* from the inode no to blockno */
528 inodes_per_block = fs->blksz / fs->inodesz;
529 ibmap_idx = inodeno / inode_per_grp;
530
531 /* get the block no */
532 inodeno--;
Stefan Brünsbad73812016-09-17 02:10:10 +0200533 /* get block group descriptor table */
534 bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
535 blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
Michael Walle13179c22016-09-01 11:21:40 +0200536 (inodeno % inode_per_grp) / inodes_per_block;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000537
538 /* get the offset of the inode */
539 blkoff = ((inodeno) % inodes_per_block) * fs->inodesz;
540
541 /* read the block no containing the inode */
542 read_buffer = zalloc(fs->blksz);
543 if (!read_buffer)
544 goto fail;
545 start_block_address = read_buffer;
Frederic Leroye7ee0282013-06-26 18:11:25 +0200546 status = ext4fs_devread((lbaint_t)blkno * fs->sect_perblk,
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000547 0, fs->blksz, read_buffer);
548 if (status == 0)
549 goto fail;
550
551 if (ext4fs_log_journal(read_buffer, blkno))
552 goto fail;
553
554 read_buffer = read_buffer + blkoff;
555 inode_buffer = (struct ext2_inode *)read_buffer;
Stefan Brüns7d936db2016-09-06 04:36:53 +0200556 memset(inode_buffer, '\0', fs->inodesz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000557
558 /* write the inode to original position in inode table */
559 if (ext4fs_put_metadata(start_block_address, blkno))
560 goto fail;
561
562 /* update the respective inode bitmaps */
563 inodeno++;
564 ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx);
Stefan Brüns05313d12016-09-20 01:13:01 +0200565 ext4fs_bg_free_inodes_inc(bgd, fs);
Michael Walle13179c22016-09-01 11:21:40 +0200566 ext4fs_sb_free_inodes_inc(fs->sb);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000567 /* journal backup */
568 memset(journal_buffer, '\0', fs->blksz);
Stefan Brünsbad73812016-09-17 02:10:10 +0200569 status = ext4fs_devread(ext4fs_bg_get_inode_id(bgd, fs) *
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000570 fs->sect_perblk, 0, fs->blksz, journal_buffer);
571 if (status == 0)
572 goto fail;
Stefan Brünsbad73812016-09-17 02:10:10 +0200573 if (ext4fs_log_journal(journal_buffer, ext4fs_bg_get_inode_id(bgd, fs)))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000574 goto fail;
575
576 ext4fs_update();
577 ext4fs_deinit();
Łukasz Majewski900db5d2014-05-06 09:36:05 +0200578 ext4fs_reinit_global();
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000579
580 if (ext4fs_init() != 0) {
581 printf("error in File System init\n");
582 goto fail;
583 }
584
585 free(start_block_address);
586 free(journal_buffer);
587
588 return 0;
589fail:
590 free(start_block_address);
591 free(journal_buffer);
592
593 return -1;
594}
595
596int ext4fs_init(void)
597{
598 short status;
599 int i;
Michael Walle13179c22016-09-01 11:21:40 +0200600 uint32_t real_free_blocks = 0;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000601 struct ext_filesystem *fs = get_fs();
602
603 /* populate fs */
604 fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root);
Egbert Eich7b1b2552013-05-01 01:13:19 +0000605 fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000606
607 /* get the superblock */
608 fs->sb = zalloc(SUPERBLOCK_SIZE);
609 if (!fs->sb)
610 return -ENOMEM;
Egbert Eich7b1b2552013-05-01 01:13:19 +0000611 if (!ext4_read_superblock((char *)fs->sb))
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000612 goto fail;
613
614 /* init journal */
615 if (ext4fs_init_journal())
616 goto fail;
617
618 /* get total no of blockgroups */
619 fs->no_blkgrp = (uint32_t)ext4fs_div_roundup(
Michael Walle13179c22016-09-01 11:21:40 +0200620 le32_to_cpu(ext4fs_root->sblock.total_blocks)
621 - le32_to_cpu(ext4fs_root->sblock.first_data_block),
622 le32_to_cpu(ext4fs_root->sblock.blocks_per_group));
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000623
624 /* get the block group descriptor table */
625 fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1);
626 if (ext4fs_get_bgdtable() == -1) {
627 printf("Error in getting the block group descriptor table\n");
628 goto fail;
629 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000630
631 /* load all the available bitmap block of the partition */
632 fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *));
633 if (!fs->blk_bmaps)
634 goto fail;
635 for (i = 0; i < fs->no_blkgrp; i++) {
636 fs->blk_bmaps[i] = zalloc(fs->blksz);
637 if (!fs->blk_bmaps[i])
638 goto fail;
639 }
640
641 for (i = 0; i < fs->no_blkgrp; i++) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200642 struct ext2_block_group *bgd =
643 ext4fs_get_group_descriptor(fs, i);
644 status = ext4fs_devread(ext4fs_bg_get_block_id(bgd, fs) *
Frederic Leroye7ee0282013-06-26 18:11:25 +0200645 fs->sect_perblk, 0,
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000646 fs->blksz, (char *)fs->blk_bmaps[i]);
647 if (status == 0)
648 goto fail;
649 }
650
651 /* load all the available inode bitmap of the partition */
652 fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *));
653 if (!fs->inode_bmaps)
654 goto fail;
655 for (i = 0; i < fs->no_blkgrp; i++) {
656 fs->inode_bmaps[i] = zalloc(fs->blksz);
657 if (!fs->inode_bmaps[i])
658 goto fail;
659 }
660
661 for (i = 0; i < fs->no_blkgrp; i++) {
Stefan Brünsbad73812016-09-17 02:10:10 +0200662 struct ext2_block_group *bgd =
663 ext4fs_get_group_descriptor(fs, i);
664 status = ext4fs_devread(ext4fs_bg_get_inode_id(bgd, fs) *
Frederic Leroye7ee0282013-06-26 18:11:25 +0200665 fs->sect_perblk,
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000666 0, fs->blksz,
667 (char *)fs->inode_bmaps[i]);
668 if (status == 0)
669 goto fail;
670 }
671
672 /*
673 * check filesystem consistency with free blocks of file system
674 * some time we observed that superblock freeblocks does not match
675 * with the blockgroups freeblocks when improper
676 * reboot of a linux kernel
677 */
Stefan Brünsbad73812016-09-17 02:10:10 +0200678 for (i = 0; i < fs->no_blkgrp; i++) {
679 struct ext2_block_group *bgd =
680 ext4fs_get_group_descriptor(fs, i);
681 real_free_blocks = real_free_blocks +
682 ext4fs_bg_get_free_blocks(bgd, fs);
683 }
684 if (real_free_blocks != ext4fs_sb_get_free_blocks(fs->sb))
685 ext4fs_sb_set_free_blocks(fs->sb, real_free_blocks);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000686
687 return 0;
688fail:
689 ext4fs_deinit();
690
691 return -1;
692}
693
694void ext4fs_deinit(void)
695{
696 int i;
697 struct ext2_inode inode_journal;
698 struct journal_superblock_t *jsb;
Michael Walle13179c22016-09-01 11:21:40 +0200699 uint32_t blknr;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000700 struct ext_filesystem *fs = get_fs();
Michael Walle13179c22016-09-01 11:21:40 +0200701 uint32_t new_feature_incompat;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000702
703 /* free journal */
704 char *temp_buff = zalloc(fs->blksz);
705 if (temp_buff) {
706 ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
707 &inode_journal);
708 blknr = read_allocated_block(&inode_journal,
Stephen Warren02d6ca72019-01-30 12:58:05 -0700709 EXT2_JOURNAL_SUPERBLOCK, NULL);
Frederic Leroye7ee0282013-06-26 18:11:25 +0200710 ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000711 temp_buff);
712 jsb = (struct journal_superblock_t *)temp_buff;
Michael Walle13179c22016-09-01 11:21:40 +0200713 jsb->s_start = 0;
Ma Haijune0996ca2014-01-08 08:15:33 +0800714 put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000715 (struct journal_superblock_t *)temp_buff, fs->blksz);
716 free(temp_buff);
717 }
718 ext4fs_free_journal();
719
720 /* get the superblock */
Egbert Eich7b1b2552013-05-01 01:13:19 +0000721 ext4_read_superblock((char *)fs->sb);
Michael Walle13179c22016-09-01 11:21:40 +0200722 new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat);
723 new_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
724 fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000725 put_ext4((uint64_t)(SUPERBLOCK_SIZE),
726 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
727 free(fs->sb);
728 fs->sb = NULL;
729
730 if (fs->blk_bmaps) {
731 for (i = 0; i < fs->no_blkgrp; i++) {
732 free(fs->blk_bmaps[i]);
733 fs->blk_bmaps[i] = NULL;
734 }
735 free(fs->blk_bmaps);
736 fs->blk_bmaps = NULL;
737 }
738
739 if (fs->inode_bmaps) {
740 for (i = 0; i < fs->no_blkgrp; i++) {
741 free(fs->inode_bmaps[i]);
742 fs->inode_bmaps[i] = NULL;
743 }
744 free(fs->inode_bmaps);
745 fs->inode_bmaps = NULL;
746 }
747
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000748 free(fs->gdtable);
749 fs->gdtable = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000750 /*
751 * reinitiliazed the global inode and
752 * block bitmap first execution check variables
753 */
754 fs->first_pass_ibmap = 0;
755 fs->first_pass_bbmap = 0;
756 fs->curr_inode_no = 0;
757 fs->curr_blkno = 0;
758}
759
Stefan Brüns74674ed2016-09-06 04:36:55 +0200760/*
761 * Write data to filesystem blocks. Uses same optimization for
762 * contigous sectors as ext4fs_read_file
763 */
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000764static int ext4fs_write_file(struct ext2_inode *file_inode,
Jean-Jacques Hiblotd1921362019-02-13 12:15:24 +0100765 int pos, unsigned int len, const char *buf)
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000766{
767 int i;
768 int blockcnt;
Michael Walle13179c22016-09-01 11:21:40 +0200769 uint32_t filesize = le32_to_cpu(file_inode->size);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000770 struct ext_filesystem *fs = get_fs();
Egbert Eich7b1b2552013-05-01 01:13:19 +0000771 int log2blksz = fs->dev_desc->log2blksz;
772 int log2_fs_blocksize = LOG2_BLOCK_SIZE(ext4fs_root) - log2blksz;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000773 int previous_block_number = -1;
774 int delayed_start = 0;
775 int delayed_extent = 0;
776 int delayed_next = 0;
Jean-Jacques Hiblotd1921362019-02-13 12:15:24 +0100777 const char *delayed_buf = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000778
779 /* Adjust len so it we can't read past the end of the file. */
780 if (len > filesize)
781 len = filesize;
782
783 blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz;
784
785 for (i = pos / fs->blksz; i < blockcnt; i++) {
786 long int blknr;
787 int blockend = fs->blksz;
788 int skipfirst = 0;
Stephen Warren02d6ca72019-01-30 12:58:05 -0700789 blknr = read_allocated_block(file_inode, i, NULL);
Stefan Brüns74674ed2016-09-06 04:36:55 +0200790 if (blknr <= 0)
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000791 return -1;
792
Egbert Eich7b1b2552013-05-01 01:13:19 +0000793 blknr = blknr << log2_fs_blocksize;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000794
795 if (blknr) {
796 if (previous_block_number != -1) {
797 if (delayed_next == blknr) {
798 delayed_extent += blockend;
Egbert Eich7b1b2552013-05-01 01:13:19 +0000799 delayed_next += blockend >> log2blksz;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000800 } else { /* spill */
Egbert Eich7b1b2552013-05-01 01:13:19 +0000801 put_ext4((uint64_t)
Ma Haijune0996ca2014-01-08 08:15:33 +0800802 ((uint64_t)delayed_start << log2blksz),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000803 delayed_buf,
804 (uint32_t) delayed_extent);
805 previous_block_number = blknr;
806 delayed_start = blknr;
807 delayed_extent = blockend;
808 delayed_buf = buf;
809 delayed_next = blknr +
Egbert Eich7b1b2552013-05-01 01:13:19 +0000810 (blockend >> log2blksz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000811 }
812 } else {
813 previous_block_number = blknr;
814 delayed_start = blknr;
815 delayed_extent = blockend;
816 delayed_buf = buf;
817 delayed_next = blknr +
Egbert Eich7b1b2552013-05-01 01:13:19 +0000818 (blockend >> log2blksz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000819 }
820 } else {
821 if (previous_block_number != -1) {
822 /* spill */
Ma Haijune0996ca2014-01-08 08:15:33 +0800823 put_ext4((uint64_t) ((uint64_t)delayed_start <<
Egbert Eich7b1b2552013-05-01 01:13:19 +0000824 log2blksz),
825 delayed_buf,
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000826 (uint32_t) delayed_extent);
827 previous_block_number = -1;
828 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000829 }
830 buf += fs->blksz - skipfirst;
831 }
832 if (previous_block_number != -1) {
833 /* spill */
Ma Haijune0996ca2014-01-08 08:15:33 +0800834 put_ext4((uint64_t) ((uint64_t)delayed_start << log2blksz),
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000835 delayed_buf, (uint32_t) delayed_extent);
836 previous_block_number = -1;
837 }
838
839 return len;
840}
841
Jean-Jacques Hiblotd1921362019-02-13 12:15:24 +0100842int ext4fs_write(const char *fname, const char *buffer,
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100843 unsigned long sizebytes, int type)
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000844{
845 int ret = 0;
846 struct ext2_inode *file_inode = NULL;
developer3a7e1a42024-03-19 17:20:40 +0800847 struct ext2_inode *existing_file_inode = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000848 unsigned char *inode_buffer = NULL;
849 int parent_inodeno;
850 int inodeno;
851 time_t timestamp = 0;
852
853 uint64_t bytes_reqd_for_file;
854 unsigned int blks_reqd_for_file;
855 unsigned int blocks_remaining;
856 int existing_file_inodeno;
857 char *temp_ptr = NULL;
858 long int itable_blkno;
859 long int parent_itable_blkno;
860 long int blkoff;
861 struct ext2_sblock *sblock = &(ext4fs_root->sblock);
862 unsigned int inodes_per_block;
863 unsigned int ibmap_idx;
Stefan Brünsbad73812016-09-17 02:10:10 +0200864 struct ext2_block_group *bgd = NULL;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000865 struct ext_filesystem *fs = get_fs();
866 ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100867 bool store_link_in_inode = false;
Jeroen Hofsteef978b002014-06-09 15:29:00 +0200868 memset(filename, 0x00, 256);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000869
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100870 if (type != FILETYPE_REG && type != FILETYPE_SYMLINK)
871 return -1;
872
Stefan Brüns7d936db2016-09-06 04:36:53 +0200873 g_parent_inode = zalloc(fs->inodesz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000874 if (!g_parent_inode)
875 goto fail;
876
877 if (ext4fs_init() != 0) {
878 printf("error in File System init\n");
879 return -1;
880 }
Sébastien Szymanskif7d49682019-03-22 09:33:52 +0100881
882 if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
883 printf("Unsupported feature metadata_csum found, not writing.\n");
884 return -1;
885 }
886
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000887 inodes_per_block = fs->blksz / fs->inodesz;
888 parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE);
889 if (parent_inodeno == -1)
890 goto fail;
891 if (ext4fs_iget(parent_inodeno, g_parent_inode))
892 goto fail;
Stefan Brüns57db8992016-09-06 04:36:45 +0200893 /* do not mess up a directory using hash trees */
894 if (le32_to_cpu(g_parent_inode->flags) & EXT4_INDEX_FL) {
895 printf("hash tree directory\n");
896 goto fail;
897 }
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000898 /* check if the filename is already present in root */
Stefan Brüns278f5d32016-09-06 04:36:41 +0200899 existing_file_inodeno = ext4fs_filename_unlink(filename);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000900 if (existing_file_inodeno != -1) {
developer3a7e1a42024-03-19 17:20:40 +0800901 existing_file_inode = (struct ext2_inode *)zalloc(fs->inodesz);
902 if (!existing_file_inode)
903 goto fail;
904 ret = ext4fs_iget(existing_file_inodeno, existing_file_inode);
905 if (ret) {
906 free(existing_file_inode);
907 goto fail;
908 }
909
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000910 ret = ext4fs_delete_file(existing_file_inodeno);
911 fs->first_pass_bbmap = 0;
912 fs->curr_blkno = 0;
913
914 fs->first_pass_ibmap = 0;
915 fs->curr_inode_no = 0;
916 if (ret)
917 goto fail;
918 }
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100919
920 /* calculate how many blocks required */
921 if (type == FILETYPE_SYMLINK &&
922 sizebytes <= sizeof(file_inode->b.symlink)) {
923 store_link_in_inode = true;
924 bytes_reqd_for_file = 0;
925 } else {
926 bytes_reqd_for_file = sizebytes;
927 }
928
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000929 blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz);
930 if (do_div(bytes_reqd_for_file, fs->blksz) != 0) {
931 blks_reqd_for_file++;
932 debug("total bytes for a file %u\n", blks_reqd_for_file);
933 }
934 blocks_remaining = blks_reqd_for_file;
935 /* test for available space in partition */
Michael Walle13179c22016-09-01 11:21:40 +0200936 if (le32_to_cpu(fs->sb->free_blocks) < blks_reqd_for_file) {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000937 printf("Not enough space on partition !!!\n");
938 goto fail;
939 }
940
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100941 inodeno = ext4fs_update_parent_dentry(filename, type);
Stefan Brünsc1020682016-09-06 04:36:42 +0200942 if (inodeno == -1)
943 goto fail;
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000944 /* prepare file inode */
945 inode_buffer = zalloc(fs->inodesz);
946 if (!inode_buffer)
947 goto fail;
948 file_inode = (struct ext2_inode *)inode_buffer;
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100949 file_inode->size = cpu_to_le32(sizebytes);
950 if (type == FILETYPE_SYMLINK) {
951 file_inode->mode = cpu_to_le16(S_IFLNK | S_IRWXU | S_IRWXG |
952 S_IRWXO);
953 if (store_link_in_inode) {
954 strncpy(file_inode->b.symlink, buffer, sizebytes);
955 sizebytes = 0;
956 }
957 } else {
developer3a7e1a42024-03-19 17:20:40 +0800958 if (existing_file_inode) {
959 file_inode->mode = existing_file_inode->mode;
960 } else {
961 file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | S_IRGRP |
962 S_IROTH | S_IXGRP | S_IXOTH);
963 }
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +0100964 }
developer3a7e1a42024-03-19 17:20:40 +0800965 if (existing_file_inode)
966 free(existing_file_inode);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000967 /* ToDo: Update correct time */
Michael Walle13179c22016-09-01 11:21:40 +0200968 file_inode->mtime = cpu_to_le32(timestamp);
969 file_inode->atime = cpu_to_le32(timestamp);
970 file_inode->ctime = cpu_to_le32(timestamp);
971 file_inode->nlinks = cpu_to_le16(1);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000972
973 /* Allocate data blocks */
974 ext4fs_allocate_blocks(file_inode, blocks_remaining,
975 &blks_reqd_for_file);
Michael Walle13179c22016-09-01 11:21:40 +0200976 file_inode->blockcnt = cpu_to_le32((blks_reqd_for_file * fs->blksz) >>
Marek Szyprowski07a39ab2019-06-21 15:32:51 +0200977 LOG2_SECTOR_SIZE);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000978
979 temp_ptr = zalloc(fs->blksz);
980 if (!temp_ptr)
981 goto fail;
Michael Walle13179c22016-09-01 11:21:40 +0200982 ibmap_idx = inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000983 inodeno--;
Stefan Brünsbad73812016-09-17 02:10:10 +0200984 bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
985 itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
Michael Wallec07cdcb2016-08-29 10:46:44 +0200986 (inodeno % le32_to_cpu(sblock->inodes_per_group)) /
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000987 inodes_per_block;
988 blkoff = (inodeno % inodes_per_block) * fs->inodesz;
Frederic Leroye7ee0282013-06-26 18:11:25 +0200989 ext4fs_devread((lbaint_t)itable_blkno * fs->sect_perblk, 0, fs->blksz,
990 temp_ptr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000991 if (ext4fs_log_journal(temp_ptr, itable_blkno))
992 goto fail;
993
994 memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz);
995 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
996 goto fail;
997 /* copy the file content into data blocks */
Jean-Jacques Hiblotd1921362019-02-13 12:15:24 +0100998 if (ext4fs_write_file(file_inode, 0, sizebytes, buffer) == -1) {
Simon Glass6c8f3aa2012-12-26 09:53:28 +0000999 printf("Error in copying content\n");
Stefan Brüns74674ed2016-09-06 04:36:55 +02001000 /* FIXME: Deallocate data blocks */
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001001 goto fail;
1002 }
Michael Walle13179c22016-09-01 11:21:40 +02001003 ibmap_idx = parent_inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001004 parent_inodeno--;
Stefan Brünsbad73812016-09-17 02:10:10 +02001005 bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
1006 parent_itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001007 (parent_inodeno %
Michael Wallec07cdcb2016-08-29 10:46:44 +02001008 le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001009 blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz;
1010 if (parent_itable_blkno != itable_blkno) {
1011 memset(temp_ptr, '\0', fs->blksz);
Frederic Leroye7ee0282013-06-26 18:11:25 +02001012 ext4fs_devread((lbaint_t)parent_itable_blkno * fs->sect_perblk,
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001013 0, fs->blksz, temp_ptr);
1014 if (ext4fs_log_journal(temp_ptr, parent_itable_blkno))
1015 goto fail;
1016
Stefan Brüns7d936db2016-09-06 04:36:53 +02001017 memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001018 if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno))
1019 goto fail;
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001020 } else {
1021 /*
1022 * If parent and child fall in same inode table block
1023 * both should be kept in 1 buffer
1024 */
Stefan Brüns7d936db2016-09-06 04:36:53 +02001025 memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001026 gd_index--;
1027 if (ext4fs_put_metadata(temp_ptr, itable_blkno))
1028 goto fail;
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001029 }
1030 ext4fs_update();
1031 ext4fs_deinit();
1032
1033 fs->first_pass_bbmap = 0;
1034 fs->curr_blkno = 0;
1035 fs->first_pass_ibmap = 0;
1036 fs->curr_inode_no = 0;
1037 free(inode_buffer);
1038 free(g_parent_inode);
Stefan Brüns338a5292016-09-06 04:36:51 +02001039 free(temp_ptr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001040 g_parent_inode = NULL;
1041
1042 return 0;
1043fail:
1044 ext4fs_deinit();
1045 free(inode_buffer);
1046 free(g_parent_inode);
Stefan Brüns338a5292016-09-06 04:36:51 +02001047 free(temp_ptr);
Simon Glass6c8f3aa2012-12-26 09:53:28 +00001048 g_parent_inode = NULL;
1049
1050 return -1;
1051}
Suriyan Ramasamib3a2d5a2014-11-17 14:39:36 -08001052
1053int ext4_write_file(const char *filename, void *buf, loff_t offset,
1054 loff_t len, loff_t *actwrite)
1055{
1056 int ret;
1057
1058 if (offset != 0) {
1059 printf("** Cannot support non-zero offset **\n");
1060 return -1;
1061 }
1062
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +01001063 ret = ext4fs_write(filename, buf, len, FILETYPE_REG);
Suriyan Ramasamib3a2d5a2014-11-17 14:39:36 -08001064 if (ret) {
1065 printf("** Error ext4fs_write() **\n");
1066 goto fail;
1067 }
Suriyan Ramasamib3a2d5a2014-11-17 14:39:36 -08001068
Przemyslaw Marczak2b1d6c92015-02-17 15:31:52 +01001069 *actwrite = len;
1070
Suriyan Ramasamib3a2d5a2014-11-17 14:39:36 -08001071 return 0;
1072
1073fail:
Przemyslaw Marczak2b1d6c92015-02-17 15:31:52 +01001074 *actwrite = 0;
Suriyan Ramasamib3a2d5a2014-11-17 14:39:36 -08001075
1076 return -1;
1077}
Jean-Jacques Hiblot448dd272019-02-13 12:15:25 +01001078
1079int ext4fs_create_link(const char *target, const char *fname)
1080{
1081 return ext4fs_write(fname, target, strlen(target), FILETYPE_SYMLINK);
1082}