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