New targets for:
[lwext4.git] / lwext4 / ext4_extent.c
1 /*\r
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)\r
3  *\r
4  *\r
5  * HelenOS:\r
6  * Copyright (c) 2012 Martin Sucha\r
7  * Copyright (c) 2012 Frantisek Princ\r
8  * All rights reserved.\r
9  *\r
10  * Redistribution and use in source and binary forms, with or without\r
11  * modification, are permitted provided that the following conditions\r
12  * are met:\r
13  *\r
14  * - Redistributions of source code must retain the above copyright\r
15  *   notice, this list of conditions and the following disclaimer.\r
16  * - Redistributions in binary form must reproduce the above copyright\r
17  *   notice, this list of conditions and the following disclaimer in the\r
18  *   documentation and/or other materials provided with the distribution.\r
19  * - The name of the author may not be used to endorse or promote products\r
20  *   derived from this software without specific prior written permission.\r
21  *\r
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
32  */\r
33 \r
34 /** @addtogroup lwext4\r
35  * @{\r
36  */\r
37 /**\r
38  * @file  ext4_extent.c\r
39  * @brief More complex filesystem functions.\r
40  */\r
41 \r
42 #include <ext4_config.h>\r
43 #include <ext4_extent.h>\r
44 #include <ext4_inode.h>\r
45 #include <ext4_super.h>\r
46 #include <ext4_blockdev.h>\r
47 #include <ext4_balloc.h>\r
48 \r
49 #include <string.h>\r
50 #include <stdlib.h>\r
51 \r
52 uint32_t ext4_extent_get_first_block(struct ext4_extent *extent)\r
53 {\r
54     return to_le32(extent->first_block);\r
55 }\r
56 \r
57 \r
58 void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock)\r
59 {\r
60     extent->first_block = to_le32(iblock);\r
61 }\r
62 \r
63 \r
64 uint16_t ext4_extent_get_block_count(struct ext4_extent *extent)\r
65 {\r
66     return to_le16(extent->block_count);\r
67 }\r
68 \r
69 \r
70 void ext4_extent_set_block_count(struct ext4_extent *extent, uint16_t count)\r
71 {\r
72     extent->block_count = to_le16(count);\r
73 }\r
74 \r
75 \r
76 uint64_t ext4_extent_get_start(struct ext4_extent *extent)\r
77 {\r
78     return ((uint64_t)to_le16(extent->start_hi)) << 32 |\r
79         ((uint64_t)to_le32(extent->start_lo));\r
80 }\r
81 \r
82 \r
83 void ext4_extent_set_start(struct ext4_extent *extent, uint64_t fblock)\r
84 {\r
85     extent->start_lo = to_le32((fblock << 32) >> 32);\r
86     extent->start_hi = to_le16((uint16_t)(fblock >> 32));\r
87 }\r
88 \r
89 \r
90 uint32_t ext4_extent_index_get_first_block(struct ext4_extent_index *index)\r
91 {\r
92     return to_le32(index->first_block);\r
93 }\r
94 \r
95 \r
96 void ext4_extent_index_set_first_block(struct ext4_extent_index *index,\r
97     uint32_t iblock)\r
98 {\r
99     index->first_block = to_le32(iblock);\r
100 }\r
101 \r
102 \r
103 uint64_t ext4_extent_index_get_leaf(struct ext4_extent_index *index)\r
104 {\r
105     return ((uint64_t) to_le16(index->leaf_hi)) << 32 |\r
106         ((uint64_t)to_le32(index->leaf_lo));\r
107 }\r
108 \r
109 void ext4_extent_index_set_leaf(struct ext4_extent_index *index,\r
110     uint64_t fblock)\r
111 {\r
112     index->leaf_lo = to_le32((fblock << 32) >> 32);\r
113     index->leaf_hi = to_le16((uint16_t) (fblock >> 32));\r
114 }\r
115 \r
116 \r
117 uint16_t ext4_extent_header_get_magic(struct ext4_extent_header *header)\r
118 {\r
119     return to_le16(header->magic);\r
120 }\r
121 \r
122 \r
123 void ext4_extent_header_set_magic(struct ext4_extent_header *header,\r
124     uint16_t magic)\r
125 {\r
126     header->magic = to_le16(magic);\r
127 }\r
128 \r
129 \r
130 uint16_t ext4_extent_header_get_entries_count(struct ext4_extent_header *header)\r
131 {\r
132     return to_le16(header->entries_count);\r
133 }\r
134 \r
135 \r
136 void ext4_extent_header_set_entries_count(struct ext4_extent_header *header,\r
137     uint16_t count)\r
138 {\r
139     header->entries_count = to_le16(count);\r
140 }\r
141 \r
142 \r
143 uint16_t ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header)\r
144 {\r
145     return to_le16(header->max_entries_count);\r
146 }\r
147 \r
148 \r
149 void ext4_extent_header_set_max_entries_count(struct ext4_extent_header *header,\r
150     uint16_t max_count)\r
151 {\r
152     header->max_entries_count = to_le16(max_count);\r
153 }\r
154 \r
155 \r
156 uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header)\r
157 {\r
158     return to_le16(header->depth);\r
159 }\r
160 \r
161 \r
162 void ext4_extent_header_set_depth(struct ext4_extent_header *header,\r
163     uint16_t depth)\r
164 {\r
165     header->depth = to_le16(depth);\r
166 }\r
167 \r
168 \r
169 uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header)\r
170 {\r
171     return to_le32(header->generation);\r
172 }\r
173 \r
174 \r
175 void ext4_extent_header_set_generation(struct ext4_extent_header *header,\r
176     uint32_t generation)\r
177 {\r
178     header->generation = to_le32(generation);\r
179 }\r
180 \r
181 /**@brief Binary search in extent index node.\r
182  * @param header Extent header of index node\r
183  * @param index  Output value - found index will be set here\r
184  * @param iblock Logical block number to find in index node */\r
185 static void ext4_extent_binsearch_idx(struct ext4_extent_header *header,\r
186     struct ext4_extent_index **index, uint32_t iblock)\r
187 {\r
188     struct ext4_extent_index *r;\r
189     struct ext4_extent_index *l;\r
190     struct ext4_extent_index *m;\r
191 \r
192     uint16_t entries_count =\r
193         ext4_extent_header_get_entries_count(header);\r
194 \r
195     /* Initialize bounds */\r
196     l = EXT4_EXTENT_FIRST_INDEX(header) + 1;\r
197     r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1;\r
198 \r
199     /* Do binary search */\r
200     while (l <= r) {\r
201         m = l + (r - l) / 2;\r
202         uint32_t first_block = ext4_extent_index_get_first_block(m);\r
203 \r
204         if (iblock < first_block)\r
205             r = m - 1;\r
206         else\r
207             l = m + 1;\r
208     }\r
209 \r
210     /* Set output value */\r
211     *index = l - 1;\r
212 }\r
213 \r
214 /**@brief Binary search in extent leaf node.\r
215  * @param header Extent header of leaf node\r
216  * @param extent Output value - found extent will be set here,\r
217  *               or NULL if node is empty\r
218  * @param iblock Logical block number to find in leaf node */\r
219 static void ext4_extent_binsearch(struct ext4_extent_header *header,\r
220     struct ext4_extent **extent, uint32_t iblock)\r
221 {\r
222     struct ext4_extent *r;\r
223     struct ext4_extent *l;\r
224     struct ext4_extent *m;\r
225 \r
226     uint16_t entries_count =\r
227         ext4_extent_header_get_entries_count(header);\r
228 \r
229     if (entries_count == 0) {\r
230         /* this leaf is empty */\r
231         *extent = NULL;\r
232         return;\r
233     }\r
234 \r
235     /* Initialize bounds */\r
236     l = EXT4_EXTENT_FIRST(header) + 1;\r
237     r = EXT4_EXTENT_FIRST(header) + entries_count - 1;\r
238 \r
239     /* Do binary search */\r
240     while (l <= r) {\r
241         m = l + (r - l) / 2;\r
242         uint32_t first_block = ext4_extent_get_first_block(m);\r
243 \r
244         if (iblock < first_block)\r
245             r = m - 1;\r
246         else\r
247             l = m + 1;\r
248     }\r
249 \r
250     /* Set output value */\r
251     *extent = l - 1;\r
252 }\r
253 \r
254 \r
255 int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,\r
256     uint32_t *fblock)\r
257 {\r
258     /* Compute bound defined by i-node size */\r
259     uint64_t inode_size =\r
260         ext4_inode_get_size(&inode_ref->fs->sb, inode_ref->inode);\r
261 \r
262     uint32_t block_size =\r
263         ext4_sb_get_block_size(&inode_ref->fs->sb);\r
264 \r
265     uint32_t last_idx = (inode_size - 1) / block_size;\r
266 \r
267     /* Check if requested iblock is not over size of i-node */\r
268     if (iblock > last_idx) {\r
269         *fblock = 0;\r
270         return EOK;\r
271     }\r
272 \r
273     struct ext4_block block;\r
274     block.lb_id = 0;\r
275 \r
276     /* Walk through extent tree */\r
277     struct ext4_extent_header *header =\r
278         ext4_inode_get_extent_header(inode_ref->inode);\r
279 \r
280     while (ext4_extent_header_get_depth(header) != 0) {\r
281         /* Search index in node */\r
282         struct ext4_extent_index *index;\r
283         ext4_extent_binsearch_idx(header, &index, iblock);\r
284 \r
285         /* Load child node and set values for the next iteration */\r
286         uint64_t child = ext4_extent_index_get_leaf(index);\r
287 \r
288         if (block.lb_id)\r
289             ext4_block_set(inode_ref->fs->bdev, &block);\r
290 \r
291 \r
292         int rc = ext4_block_get(inode_ref->fs->bdev, &block, child);\r
293         if (rc != EOK)\r
294             return rc;\r
295 \r
296         header = (struct ext4_extent_header *)block.data;\r
297     }\r
298 \r
299     /* Search extent in the leaf block */\r
300     struct ext4_extent* extent = NULL;\r
301     ext4_extent_binsearch(header, &extent, iblock);\r
302 \r
303     /* Prevent empty leaf */\r
304     if (extent == NULL) {\r
305         *fblock = 0;\r
306     } else {\r
307         /* Compute requested physical block address */\r
308         uint32_t phys_block;\r
309         uint32_t first = ext4_extent_get_first_block(extent);\r
310         phys_block = ext4_extent_get_start(extent) + iblock - first;\r
311 \r
312         *fblock = phys_block;\r
313     }\r
314 \r
315     /* Cleanup */\r
316     if (block.lb_id)\r
317         ext4_block_set(inode_ref->fs->bdev, &block);\r
318 \r
319     return EOK;\r
320 }\r
321 \r
322 /**@brief Find extent for specified iblock.\r
323  * This function is used for finding block in the extent tree with\r
324  * saving the path through the tree for possible future modifications.\r
325  * @param inode_ref I-node to read extent tree from\r
326  * @param iblock    Iblock to find extent for\r
327  * @param ret_path  Output value for loaded path from extent tree\r
328  * @return Error code */\r
329 static int ext4_extent_find_extent(struct ext4_inode_ref *inode_ref,\r
330     uint32_t iblock, struct ext4_extent_path **ret_path)\r
331 {\r
332     struct ext4_extent_header *eh =\r
333         ext4_inode_get_extent_header(inode_ref->inode);\r
334 \r
335     uint16_t depth = ext4_extent_header_get_depth(eh);\r
336     uint16_t i;\r
337     struct ext4_extent_path *tmp_path;\r
338 \r
339     /* Added 2 for possible tree growing */\r
340     tmp_path = malloc(sizeof(struct ext4_extent_path) * (depth + 2));\r
341     if (tmp_path == NULL)\r
342         return ENOMEM;\r
343 \r
344     /* Initialize structure for algorithm start */\r
345     tmp_path[0].block = inode_ref->block;\r
346     tmp_path[0].header = eh;\r
347 \r
348     /* Walk through the extent tree */\r
349     uint16_t pos = 0;\r
350     int rc;\r
351     while (ext4_extent_header_get_depth(eh) != 0) {\r
352         /* Search index in index node by iblock */\r
353         ext4_extent_binsearch_idx(tmp_path[pos].header,\r
354             &tmp_path[pos].index, iblock);\r
355 \r
356         tmp_path[pos].depth = depth;\r
357         tmp_path[pos].extent = NULL;\r
358 \r
359         ext4_assert(tmp_path[pos].index != 0);\r
360 \r
361         /* Load information for the next iteration */\r
362         uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);\r
363 \r
364         struct ext4_block block;\r
365         rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);\r
366         if (rc != EOK)\r
367             goto cleanup;\r
368 \r
369         pos++;\r
370 \r
371         eh = (struct ext4_extent_header *)block.data;\r
372         tmp_path[pos].block = block;\r
373         tmp_path[pos].header = eh;\r
374     }\r
375 \r
376     tmp_path[pos].depth = 0;\r
377     tmp_path[pos].extent = NULL;\r
378     tmp_path[pos].index = NULL;\r
379 \r
380     /* Find extent in the leaf node */\r
381     ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);\r
382     *ret_path = tmp_path;\r
383 \r
384     return EOK;\r
385 \r
386 cleanup:\r
387     /*\r
388      * Put loaded blocks\r
389      * From 1: 0 is a block with inode data\r
390      */\r
391     for (i = 1; i < tmp_path->depth; ++i) {\r
392         if (tmp_path[i].block.lb_id)\r
393             ext4_block_set(inode_ref->fs->bdev, &tmp_path[i].block);\r
394     }\r
395 \r
396     /* Destroy temporary data structure */\r
397     free(tmp_path);\r
398 \r
399     return rc;\r
400 }\r
401 \r
402 /**@brief Release extent and all data blocks covered by the extent.\r
403  * @param inode_ref I-node to release extent and block from\r
404  * @param extent    Extent to release\r
405  * @return Error code */\r
406 static int ext4_extent_release(struct ext4_inode_ref *inode_ref,\r
407     struct ext4_extent *extent)\r
408 {\r
409     /* Compute number of the first physical block to release */\r
410     uint64_t start = ext4_extent_get_start(extent);\r
411     uint16_t block_count = ext4_extent_get_block_count(extent);\r
412 \r
413     return ext4_balloc_free_blocks(inode_ref, start, block_count);\r
414 }\r
415 \r
416 /** Recursively release the whole branch of the extent tree.\r
417  * For each entry of the node release the subbranch and finally release\r
418  * the node. In the leaf node all extents will be released.\r
419  * @param inode_ref I-node where the branch is released\r
420  * @param index     Index in the non-leaf node to be released\r
421  *                  with the whole subtree\r
422  * @return Error code */\r
423 static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,\r
424         struct ext4_extent_index *index)\r
425 {\r
426     uint32_t fblock = ext4_extent_index_get_leaf(index);\r
427     uint32_t i;\r
428     struct ext4_block block;\r
429     int rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);\r
430     if (rc != EOK)\r
431         return rc;\r
432 \r
433     struct ext4_extent_header *header = (void *)block.data;\r
434 \r
435     if (ext4_extent_header_get_depth(header)) {\r
436         /* The node is non-leaf, do recursion */\r
437         struct ext4_extent_index *idx = EXT4_EXTENT_FIRST_INDEX(header);\r
438 \r
439         /* Release all subbranches */\r
440         for (i = 0; i < ext4_extent_header_get_entries_count(header);\r
441             ++i, ++idx) {\r
442             rc = ext4_extent_release_branch(inode_ref, idx);\r
443             if (rc != EOK)\r
444                 return rc;\r
445         }\r
446     } else {\r
447         /* Leaf node reached */\r
448         struct ext4_extent *ext = EXT4_EXTENT_FIRST(header);\r
449 \r
450         /* Release all extents and stop recursion */\r
451         for (i = 0; i < ext4_extent_header_get_entries_count(header);\r
452             ++i, ++ext) {\r
453             rc = ext4_extent_release(inode_ref, ext);\r
454             if (rc != EOK)\r
455                 return rc;\r
456         }\r
457     }\r
458 \r
459     /* Release data block where the node was stored */\r
460 \r
461     rc = ext4_block_set(inode_ref->fs->bdev, &block);\r
462     if (rc != EOK)\r
463         return rc;\r
464 \r
465     ext4_balloc_free_block(inode_ref, fblock);\r
466 \r
467     return EOK;\r
468 }\r
469 \r
470 \r
471 int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,\r
472     uint32_t iblock_from)\r
473 {\r
474     /* Find the first extent to modify */\r
475     struct ext4_extent_path *path;\r
476     uint16_t i;\r
477     int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);\r
478     if (rc != EOK)\r
479         return rc;\r
480 \r
481     /* Jump to last item of the path (extent) */\r
482     struct ext4_extent_path *path_ptr = path;\r
483     while (path_ptr->depth != 0)\r
484         path_ptr++;\r
485 \r
486     ext4_assert(path_ptr->extent != NULL);\r
487 \r
488     /* First extent maybe released partially */\r
489     uint32_t first_iblock =\r
490         ext4_extent_get_first_block(path_ptr->extent);\r
491     uint32_t first_fblock =\r
492         ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock;\r
493 \r
494     uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);\r
495 \r
496     uint16_t delete_count = block_count -\r
497         (ext4_extent_get_start(path_ptr->extent) - first_fblock);\r
498 \r
499     /* Release all blocks */\r
500     rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);\r
501     if (rc != EOK)\r
502         goto cleanup;\r
503 \r
504     /* Correct counter */\r
505     block_count -= delete_count;\r
506     ext4_extent_set_block_count(path_ptr->extent, block_count);\r
507 \r
508     /* Initialize the following loop */\r
509     uint16_t entries =\r
510         ext4_extent_header_get_entries_count(path_ptr->header);\r
511     struct ext4_extent *tmp_ext = path_ptr->extent + 1;\r
512     struct ext4_extent *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;\r
513 \r
514     /* If first extent empty, release it */\r
515     if (block_count == 0)\r
516         entries--;\r
517 \r
518     /* Release all successors of the first extent in the same node */\r
519     while (tmp_ext < stop_ext) {\r
520         first_fblock = ext4_extent_get_start(tmp_ext);\r
521         delete_count = ext4_extent_get_block_count(tmp_ext);\r
522 \r
523         rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);\r
524         if (rc != EOK)\r
525             goto cleanup;\r
526 \r
527         entries--;\r
528         tmp_ext++;\r
529     }\r
530 \r
531     ext4_extent_header_set_entries_count(path_ptr->header, entries);\r
532     path_ptr->block.dirty = true;\r
533 \r
534     /* If leaf node is empty, parent entry must be modified */\r
535     bool remove_parent_record = false;\r
536 \r
537     /* Don't release root block (including inode data) !!! */\r
538     if ((path_ptr != path) && (entries == 0)) {\r
539         rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id);\r
540         if (rc != EOK)\r
541             goto cleanup;\r
542 \r
543         remove_parent_record = true;\r
544     }\r
545 \r
546     /* Jump to the parent */\r
547     --path_ptr;\r
548 \r
549     /* Release all successors in all tree levels */\r
550     while (path_ptr >= path) {\r
551         entries = ext4_extent_header_get_entries_count(path_ptr->header);\r
552         struct ext4_extent_index *index = path_ptr->index + 1;\r
553         struct ext4_extent_index *stop =\r
554             EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;\r
555 \r
556         /* Correct entries count because of changes in the previous iteration */\r
557         if (remove_parent_record)\r
558             entries--;\r
559 \r
560         /* Iterate over all entries and release the whole subtrees */\r
561         while (index < stop) {\r
562             rc = ext4_extent_release_branch(inode_ref, index);\r
563             if (rc != EOK)\r
564                 goto cleanup;\r
565 \r
566             ++index;\r
567             --entries;\r
568         }\r
569 \r
570         ext4_extent_header_set_entries_count(path_ptr->header, entries);\r
571         path_ptr->block.dirty = true;\r
572 \r
573         /* Free the node if it is empty */\r
574         if ((entries == 0) && (path_ptr != path)) {\r
575             rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id);\r
576             if (rc != EOK)\r
577                 goto cleanup;\r
578 \r
579             /* Mark parent to be checked */\r
580             remove_parent_record = true;\r
581         } else\r
582             remove_parent_record = false;\r
583 \r
584         --path_ptr;\r
585     }\r
586 \r
587     if(!entries)\r
588         ext4_extent_header_set_depth(path->header, 0);\r
589 \r
590 cleanup:\r
591     /*\r
592      * Put loaded blocks\r
593      * starting from 1: 0 is a block with inode data\r
594      */\r
595     for (i = 1; i <= path->depth; ++i) {\r
596         if (path[i].block.lb_id)\r
597             ext4_block_set(inode_ref->fs->bdev, &path[i].block);\r
598     }\r
599 \r
600     /* Destroy temporary data structure */\r
601     free(path);\r
602 \r
603     return rc;\r
604 }\r
605 \r
606 /**@brief Append new extent to the i-node and do some splitting if necessary.\r
607  * @param inode_ref      I-node to append extent to\r
608  * @param path           Path in the extent tree for possible splitting\r
609  * @param last_path_item Input/output parameter for pointer to the last\r
610  *                       valid item in the extent tree path\r
611  * @param iblock         Logical index of block to append extent for\r
612  * @return Error code */\r
613 static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,\r
614     struct ext4_extent_path *path, uint32_t iblock)\r
615 {\r
616     struct ext4_extent_path *path_ptr = path + path->depth;\r
617 \r
618     uint32_t block_size =\r
619         ext4_sb_get_block_size(&inode_ref->fs->sb);\r
620 \r
621     /* Start splitting */\r
622     while (path_ptr > path) {\r
623         uint16_t entries =\r
624             ext4_extent_header_get_entries_count(path_ptr->header);\r
625         uint16_t limit =\r
626             ext4_extent_header_get_max_entries_count(path_ptr->header);\r
627 \r
628         if (entries == limit) {\r
629             /* Full node - allocate block for new one */\r
630             uint32_t fblock;\r
631             int rc = ext4_balloc_alloc_block(inode_ref, &fblock);\r
632             if (rc != EOK)\r
633                 return rc;\r
634 \r
635             struct ext4_block block;\r
636             rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);\r
637             if (rc != EOK) {\r
638                 ext4_balloc_free_block(inode_ref, fblock);\r
639                 return rc;\r
640             }\r
641 \r
642             /* Put back not modified old block */\r
643             ext4_block_set(inode_ref->fs->bdev, &path_ptr->block);\r
644 \r
645             /* Initialize newly allocated block and remember it */\r
646             memset(block.data, 0, block_size);\r
647             path_ptr->block = block;\r
648 \r
649             /* Update pointers in extent path structure */\r
650             path_ptr->header = (void *)block.data;\r
651             if (path_ptr->depth) {\r
652                 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);\r
653                 ext4_extent_index_set_first_block(path_ptr->index, iblock);\r
654                 ext4_extent_index_set_leaf(path_ptr->index,\r
655                     (path_ptr + 1)->block.lb_id);\r
656                 limit = (block_size - sizeof(struct ext4_extent_header)) /\r
657                     sizeof(struct ext4_extent_index);\r
658             } else {\r
659                 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);\r
660                 ext4_extent_set_first_block(path_ptr->extent, iblock);\r
661                 limit = (block_size - sizeof(struct ext4_extent_header)) /\r
662                     sizeof(struct ext4_extent);\r
663             }\r
664 \r
665             /* Initialize on-disk structure (header) */\r
666             ext4_extent_header_set_entries_count(path_ptr->header, 1);\r
667             ext4_extent_header_set_max_entries_count(path_ptr->header, limit);\r
668             ext4_extent_header_set_magic(path_ptr->header, EXT4_EXTENT_MAGIC);\r
669             ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth);\r
670             ext4_extent_header_set_generation(path_ptr->header, 0);\r
671 \r
672             path_ptr->block.dirty = true;\r
673 \r
674             /* Jump to the preceeding item */\r
675             path_ptr--;\r
676         } else {\r
677             /* Node with free space */\r
678             if (path_ptr->depth) {\r
679                 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;\r
680                 ext4_extent_index_set_first_block(path_ptr->index, iblock);\r
681                 ext4_extent_index_set_leaf(path_ptr->index,\r
682                     (path_ptr + 1)->block.lb_id);\r
683             } else {\r
684                 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;\r
685                 ext4_extent_set_first_block(path_ptr->extent, iblock);\r
686             }\r
687 \r
688             ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);\r
689             path_ptr->block.dirty = true;\r
690 \r
691             /* No more splitting needed */\r
692             return EOK;\r
693         }\r
694     }\r
695 \r
696     ext4_assert(path_ptr == path);\r
697 \r
698     /* Should be the root split too? */\r
699 \r
700     uint16_t entries = ext4_extent_header_get_entries_count(path->header);\r
701     uint16_t limit = ext4_extent_header_get_max_entries_count(path->header);\r
702 \r
703     if (entries == limit) {\r
704         uint32_t new_fblock;\r
705         int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);\r
706         if (rc != EOK)\r
707             return rc;\r
708 \r
709         struct ext4_block block;\r
710         rc = ext4_block_get(inode_ref->fs->bdev, &block, new_fblock);\r
711         if (rc != EOK)\r
712             return rc;\r
713 \r
714         /* Initialize newly allocated block */\r
715         memset(block.data, 0, block_size);\r
716 \r
717         /* Move data from root to the new block */\r
718         memcpy(block.data, inode_ref->inode->blocks,\r
719             EXT4_INODE_BLOCKS * sizeof(uint32_t));\r
720 \r
721         /* Data block is initialized */\r
722 \r
723         struct ext4_block *root_block = &path->block;\r
724         uint16_t root_depth = path->depth;\r
725         struct ext4_extent_header *root_header = path->header;\r
726 \r
727         /* Make space for tree growing */\r
728         struct ext4_extent_path *new_root = path;\r
729         struct ext4_extent_path *old_root = path + 1;\r
730 \r
731         size_t nbytes = sizeof(struct ext4_extent_path) * (path->depth + 1);\r
732         memmove(old_root, new_root, nbytes);\r
733         memset(new_root, 0, sizeof(struct ext4_extent_path));\r
734 \r
735         /* Update old root structure */\r
736         old_root->block = block;\r
737         old_root->header = (struct ext4_extent_header *)block.data;\r
738 \r
739         /* Add new entry and update limit for entries */\r
740         if (old_root->depth) {\r
741             limit = (block_size - sizeof(struct ext4_extent_header)) /\r
742                 sizeof(struct ext4_extent_index);\r
743             old_root->index = EXT4_EXTENT_FIRST_INDEX(old_root->header) + entries;\r
744             ext4_extent_index_set_first_block(old_root->index, iblock);\r
745             ext4_extent_index_set_leaf(old_root->index,\r
746                 (old_root + 1)->block.lb_id);\r
747             old_root->extent = NULL;\r
748         } else {\r
749             limit = (block_size - sizeof(struct ext4_extent_header)) /\r
750                 sizeof(struct ext4_extent);\r
751             old_root->extent = EXT4_EXTENT_FIRST(old_root->header) + entries;\r
752             ext4_extent_set_first_block(old_root->extent, iblock);\r
753             old_root->index = NULL;\r
754         }\r
755 \r
756         ext4_extent_header_set_entries_count(old_root->header, entries + 1);\r
757         ext4_extent_header_set_max_entries_count(old_root->header, limit);\r
758 \r
759         old_root->block.dirty = true;\r
760 \r
761         /* Re-initialize new root metadata */\r
762         new_root->depth = root_depth + 1;\r
763         new_root->block = *root_block;\r
764         new_root->header = root_header;\r
765         new_root->extent = NULL;\r
766         new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header);\r
767 \r
768         ext4_extent_header_set_depth(new_root->header, new_root->depth);\r
769 \r
770         /* Create new entry in root */\r
771         ext4_extent_header_set_entries_count(new_root->header, 1);\r
772         ext4_extent_index_set_first_block(new_root->index, 0);\r
773         ext4_extent_index_set_leaf(new_root->index, new_fblock);\r
774 \r
775         new_root->block.dirty = true;\r
776     } else {\r
777         if (path->depth) {\r
778             path->index = EXT4_EXTENT_FIRST_INDEX(path->header) + entries;\r
779             ext4_extent_index_set_first_block(path->index, iblock);\r
780             ext4_extent_index_set_leaf(path->index, (path + 1)->block.lb_id);\r
781         } else {\r
782             path->extent = EXT4_EXTENT_FIRST(path->header) + entries;\r
783             ext4_extent_set_first_block(path->extent, iblock);\r
784         }\r
785 \r
786         ext4_extent_header_set_entries_count(path->header, entries + 1);\r
787         path->block.dirty = true;\r
788     }\r
789 \r
790     return EOK;\r
791 }\r
792 \r
793 \r
794 int ext4_extent_append_block(struct ext4_inode_ref *inode_ref,\r
795         uint32_t *iblock, uint32_t *fblock, bool update_size)\r
796 {\r
797     uint16_t i;\r
798     struct ext4_sblock *sb = &inode_ref->fs->sb;\r
799     uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);\r
800     uint32_t block_size = ext4_sb_get_block_size(sb);\r
801 \r
802     /* Calculate number of new logical block */\r
803     uint32_t new_block_idx = 0;\r
804     if (inode_size > 0) {\r
805         if ((inode_size % block_size) != 0)\r
806             inode_size += block_size - (inode_size % block_size);\r
807 \r
808         new_block_idx = inode_size / block_size;\r
809     }\r
810 \r
811     /* Load the nearest leaf (with extent) */\r
812     struct ext4_extent_path *path;\r
813     int rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);\r
814     if (rc != EOK)\r
815         return rc;\r
816 \r
817     /* Jump to last item of the path (extent) */\r
818     struct ext4_extent_path *path_ptr = path;\r
819     while (path_ptr->depth != 0)\r
820         path_ptr++;\r
821 \r
822     /* Add new extent to the node if not present */\r
823     if (path_ptr->extent == NULL)\r
824         goto append_extent;\r
825 \r
826     uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);\r
827     uint16_t block_limit = (1 << 15);\r
828 \r
829     uint32_t phys_block = 0;\r
830     if (block_count < block_limit) {\r
831         /* There is space for new block in the extent */\r
832         if (block_count == 0) {\r
833             /* Existing extent is empty */\r
834             rc = ext4_balloc_alloc_block(inode_ref, &phys_block);\r
835             if (rc != EOK)\r
836                 goto finish;\r
837 \r
838             /* Initialize extent */\r
839             ext4_extent_set_first_block(path_ptr->extent, new_block_idx);\r
840             ext4_extent_set_start(path_ptr->extent, phys_block);\r
841             ext4_extent_set_block_count(path_ptr->extent, 1);\r
842 \r
843             /* Update i-node */\r
844             if (update_size) {\r
845                 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);\r
846                 inode_ref->dirty = true;\r
847             }\r
848 \r
849             path_ptr->block.dirty = true;\r
850 \r
851             goto finish;\r
852         } else {\r
853             /* Existing extent contains some blocks */\r
854             phys_block = ext4_extent_get_start(path_ptr->extent);\r
855             phys_block += ext4_extent_get_block_count(path_ptr->extent);\r
856 \r
857             /* Check if the following block is free for allocation */\r
858             bool free;\r
859             rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);\r
860             if (rc != EOK)\r
861                 goto finish;\r
862 \r
863             if (!free) {\r
864                 /* Target is not free, new block must be appended to new extent */\r
865                 goto append_extent;\r
866             }\r
867 \r
868             /* Update extent */\r
869             ext4_extent_set_block_count(path_ptr->extent, block_count + 1);\r
870 \r
871             /* Update i-node */\r
872             if (update_size) {\r
873                 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);\r
874                 inode_ref->dirty = true;\r
875             }\r
876 \r
877             path_ptr->block.dirty = true;\r
878 \r
879             goto finish;\r
880         }\r
881     }\r
882 \r
883 \r
884 append_extent:\r
885     /* Append new extent to the tree */\r
886     phys_block = 0;\r
887 \r
888     /* Allocate new data block */\r
889     rc = ext4_balloc_alloc_block(inode_ref, &phys_block);\r
890     if (rc != EOK)\r
891         goto finish;\r
892 \r
893     /* Append extent for new block (includes tree splitting if needed) */\r
894     rc = ext4_extent_append_extent(inode_ref, path, new_block_idx);\r
895     if (rc != EOK) {\r
896         ext4_balloc_free_block(inode_ref, phys_block);\r
897         goto finish;\r
898     }\r
899 \r
900     uint32_t tree_depth = ext4_extent_header_get_depth(path->header);\r
901     path_ptr = path + tree_depth;\r
902 \r
903     /* Initialize newly created extent */\r
904     ext4_extent_set_block_count(path_ptr->extent, 1);\r
905     ext4_extent_set_first_block(path_ptr->extent, new_block_idx);\r
906     ext4_extent_set_start(path_ptr->extent, phys_block);\r
907 \r
908     /* Update i-node */\r
909     if (update_size) {\r
910         ext4_inode_set_size(inode_ref->inode, inode_size + block_size);\r
911         inode_ref->dirty = true;\r
912     }\r
913 \r
914     path_ptr->block.dirty = true;\r
915 \r
916 finish:\r
917     /* Set return values */\r
918     *iblock = new_block_idx;\r
919     *fblock = phys_block;\r
920 \r
921     /*\r
922      * Put loaded blocks\r
923      * starting from 1: 0 is a block with inode data\r
924      */\r
925     for (i = 1; i <= path->depth; ++i) {\r
926         if (path[i].block.lb_id)\r
927             ext4_block_set(inode_ref->fs->bdev, &path[i].block);\r
928     }\r
929 \r
930     /* Destroy temporary data structure */\r
931     free(path);\r
932 \r
933     return rc;\r
934 }\r
935 \r
936 /**\r
937  * @}\r
938  */\r