1. Extra directory remove caching.
[lwext4.git] / lwext4 / ext4.c
1 /*\r
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)\r
3  * All rights reserved.\r
4  *\r
5  * Redistribution and use in source and binary forms, with or without\r
6  * modification, are permitted provided that the following conditions\r
7  * are met:\r
8  *\r
9  * - Redistributions of source code must retain the above copyright\r
10  *   notice, this list of conditions and the following disclaimer.\r
11  * - Redistributions in binary form must reproduce the above copyright\r
12  *   notice, this list of conditions and the following disclaimer in the\r
13  *   documentation and/or other materials provided with the distribution.\r
14  * - The name of the author may not be used to endorse or promote products\r
15  *   derived from this software without specific prior written permission.\r
16  *\r
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  */\r
28 \r
29 /** @addtogroup lwext4\r
30  * @{\r
31  */\r
32 /**\r
33  * @file  ext4.h\r
34  * @brief Ext4 high level operations (file, directory, mountpoints...)\r
35  */\r
36 \r
37 #include <ext4_config.h>\r
38 #include <ext4_blockdev.h>\r
39 #include <ext4_types.h>\r
40 #include <ext4_debug.h>\r
41 #include <ext4_errno.h>\r
42 #include <ext4_fs.h>\r
43 #include <ext4_dir.h>\r
44 #include <ext4_inode.h>\r
45 #include <ext4_super.h>\r
46 #include <ext4_dir_idx.h>\r
47 \r
48 #include <stdlib.h>\r
49 #include <string.h>\r
50 \r
51 #include <ext4.h>\r
52 \r
53 /**@brief   Mount point OS dependent lock*/\r
54 #define EXT4_MP_LOCK(_m)    \\r
55         do { (_m)->os_locks ? (_m)->os_locks->lock() : 0; }while(0)\r
56 \r
57 /**@brief   Mount point OS dependent unlock*/\r
58 #define EXT4_MP_UNLOCK(_m)  \\r
59         do { (_m)->os_locks ? (_m)->os_locks->unlock() : 0; }while(0)\r
60 \r
61 /**@brief   Mount point descrpitor.*/\r
62 struct ext4_mountpoint {\r
63 \r
64     /**@brief   Mount point name (@ref ext4_mount)*/\r
65     const char *name;\r
66 \r
67     /**@brief   Os dependent lock/unlock functions.*/\r
68     struct ext4_lock *os_locks;\r
69 \r
70     /**@brief   Ext4 filesystem internals.*/\r
71     struct ext4_fs fs;\r
72 \r
73     /**@brief   Dynamic alocation cache flag.*/\r
74     bool cache_dynamic;\r
75 };\r
76 \r
77 /**@brief   Block devices descriptor.*/\r
78 struct _ext4_devices {\r
79 \r
80     /**@brief   Block device name (@ref ext4_device_register)*/\r
81     const char *name;\r
82 \r
83     /**@brief   Block device handle.*/\r
84     struct ext4_blockdev *bd;\r
85 \r
86     /**@brief   Block cache handle.*/\r
87     struct ext4_bcache *bc;\r
88 };\r
89 \r
90 /**@brief   Block devices.*/\r
91 struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];\r
92 \r
93 \r
94 /**@brief   Mountpoints.*/\r
95 struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];\r
96 \r
97 \r
98 \r
99 int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,\r
100     const char *dev_name)\r
101 {\r
102     uint32_t i;\r
103     ext4_assert(bd && dev_name);\r
104 \r
105     for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {\r
106         if(!_bdevices[i].name){\r
107             _bdevices[i].name   = dev_name;\r
108             _bdevices[i].bd = bd;\r
109             _bdevices[i].bc = bc;\r
110             return EOK;\r
111         }\r
112     }\r
113     return ENOSPC;\r
114 }\r
115 \r
116 /****************************************************************************/\r
117 \r
118 \r
119 static bool ext4_is_dots(const uint8_t *name, size_t name_size)\r
120 {\r
121     if ((name_size == 1) && (name[0] == '.'))\r
122         return true;\r
123 \r
124     if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))\r
125         return true;\r
126 \r
127     return false;\r
128 }\r
129 \r
130 static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)\r
131 {\r
132 \r
133     struct ext4_fs *fs = enode->fs;\r
134 \r
135     /* Check if node is directory */\r
136     if (!ext4_inode_is_type(&fs->sb, enode->inode,\r
137             EXT4_INODE_MODE_DIRECTORY)) {\r
138         *has_children = false;\r
139         return EOK;\r
140     }\r
141 \r
142     struct ext4_directory_iterator it;\r
143     int rc = ext4_dir_iterator_init(&it, enode, 0);\r
144     if (rc != EOK)\r
145         return rc;\r
146 \r
147     /* Find a non-empty directory entry */\r
148     bool found = false;\r
149     while (it.current != NULL) {\r
150         if (it.current->inode != 0) {\r
151             uint16_t name_size =\r
152                     ext4_dir_entry_ll_get_name_length(&fs->sb,\r
153                             it.current);\r
154             if (!ext4_is_dots(it.current->name, name_size)) {\r
155                 found = true;\r
156                 break;\r
157             }\r
158         }\r
159 \r
160         rc = ext4_dir_iterator_next(&it);\r
161         if (rc != EOK) {\r
162             ext4_dir_iterator_fini(&it);\r
163             return rc;\r
164         }\r
165     }\r
166 \r
167     rc = ext4_dir_iterator_fini(&it);\r
168     if (rc != EOK)\r
169         return rc;\r
170 \r
171     *has_children = found;\r
172 \r
173     return EOK;\r
174 }\r
175 \r
176 \r
177 static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,\r
178     struct ext4_inode_ref *child, const char *name, uint32_t name_len)\r
179 {\r
180     /* Check maximum name length */\r
181     if(name_len > EXT4_DIRECTORY_FILENAME_LEN)\r
182         return EINVAL;\r
183 \r
184     /* Add entry to parent directory */\r
185     int rc = ext4_dir_add_entry(parent, name, name_len,\r
186             child);\r
187     if (rc != EOK)\r
188         return rc;\r
189 \r
190     /* Fill new dir -> add '.' and '..' entries */\r
191     if (ext4_inode_is_type(&mp->fs.sb, child->inode,\r
192             EXT4_INODE_MODE_DIRECTORY)) {\r
193         rc = ext4_dir_add_entry(child, ".", strlen("."),\r
194                 child);\r
195         if (rc != EOK) {\r
196             ext4_dir_remove_entry(parent, name, strlen(name));\r
197             return rc;\r
198         }\r
199 \r
200         rc = ext4_dir_add_entry(child, "..", strlen(".."),\r
201                 parent);\r
202         if (rc != EOK) {\r
203             ext4_dir_remove_entry(parent, name, strlen(name));\r
204             ext4_dir_remove_entry(child, ".", strlen("."));\r
205             return rc;\r
206         }\r
207 \r
208 #if CONFIG_DIR_INDEX_ENABLE\r
209         /* Initialize directory index if supported */\r
210         if (ext4_sb_check_feature_compatible(&mp->fs.sb,\r
211                 EXT4_FEATURE_COMPAT_DIR_INDEX)) {\r
212             rc = ext4_dir_dx_init(child);\r
213             if (rc != EOK)\r
214                 return rc;\r
215 \r
216             ext4_inode_set_flag(child->inode,\r
217                 EXT4_INODE_FLAG_INDEX);\r
218             child->dirty = true;\r
219         }\r
220 #endif\r
221 \r
222         uint16_t parent_links =\r
223                 ext4_inode_get_links_count(parent->inode);\r
224         parent_links++;\r
225         ext4_inode_set_links_count(parent->inode, parent_links);\r
226 \r
227         parent->dirty = true;\r
228     }\r
229 \r
230     uint16_t child_links =\r
231             ext4_inode_get_links_count(child->inode);\r
232     child_links++;\r
233     ext4_inode_set_links_count(child->inode, child_links);\r
234 \r
235     child->dirty = true;\r
236 \r
237     return EOK;\r
238 }\r
239 \r
240 static int ext4_unlink(struct ext4_mountpoint *mp,\r
241     struct ext4_inode_ref *parent, struct ext4_inode_ref *child_inode_ref,\r
242     const char *name, uint32_t name_len)\r
243 {\r
244     bool has_children;\r
245     int rc = ext4_has_children(&has_children, child_inode_ref);\r
246     if (rc != EOK)\r
247         return rc;\r
248 \r
249     /* Cannot unlink non-empty node */\r
250     if (has_children)\r
251         return ENOTSUP;\r
252 \r
253     /* Remove entry from parent directory */\r
254 \r
255     rc = ext4_dir_remove_entry(parent, name, name_len);\r
256     if (rc != EOK)\r
257         return rc;\r
258 \r
259 \r
260     uint32_t lnk_count =\r
261             ext4_inode_get_links_count(child_inode_ref->inode);\r
262     lnk_count--;\r
263 \r
264     bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,\r
265             EXT4_INODE_MODE_DIRECTORY);\r
266 \r
267     /* If directory - handle links from parent */\r
268     if ((lnk_count <= 1) && (is_dir)) {\r
269         ext4_assert(lnk_count == 1);\r
270 \r
271         lnk_count--;\r
272 \r
273         uint32_t parent_lnk_count = ext4_inode_get_links_count(\r
274                 parent->inode);\r
275 \r
276         parent_lnk_count--;\r
277         ext4_inode_set_links_count(parent->inode, parent_lnk_count);\r
278 \r
279         parent->dirty = true;\r
280     }\r
281 \r
282     /*\r
283      * TODO: Update timestamps of the parent\r
284      * (when we have wall-clock time).\r
285      *\r
286      * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);\r
287      * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);\r
288      * parent->dirty = true;\r
289      */\r
290 \r
291     /*\r
292      * TODO: Update timestamp for inode.\r
293      *\r
294      * ext4_inode_set_change_inode_time(child_inode_ref->inode,\r
295      *     (uint32_t) now);\r
296      */\r
297 \r
298     ext4_inode_set_deletion_time(child_inode_ref->inode, 1);\r
299     ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);\r
300     child_inode_ref->dirty = true;\r
301 \r
302     return EOK;\r
303 }\r
304 \r
305 /****************************************************************************/\r
306 \r
307 int ext4_mount(const char * dev_name, char *mount_point)\r
308 {\r
309     ext4_assert(mount_point && dev_name);\r
310     int r = EOK;\r
311     int i;\r
312 \r
313     uint32_t bsize;\r
314     struct ext4_blockdev *bd = 0;\r
315     struct ext4_bcache *bc = 0;\r
316     struct ext4_mountpoint *mp = 0;\r
317 \r
318     if(mount_point[strlen(mount_point) - 1] != '/')\r
319         return ENOTSUP;\r
320 \r
321     for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {\r
322         if(_bdevices[i].name){\r
323             if(!strcmp(dev_name, _bdevices[i].name)){\r
324                 bd = _bdevices[i].bd;\r
325                 bc = _bdevices[i].bc;\r
326                 break;\r
327             }\r
328         }\r
329     }\r
330 \r
331     if(!bd)\r
332         return ENODEV;\r
333 \r
334     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
335         if(!_mp[i].name){\r
336             _mp[i].name = mount_point;\r
337             mp = &_mp[i];\r
338             break;\r
339         }\r
340     }\r
341 \r
342     if(!mp)\r
343         return ENOMEM;\r
344 \r
345     r = ext4_block_init(bd);\r
346     if(r != EOK)\r
347         return r;\r
348 \r
349     r = ext4_fs_init(&mp->fs, bd);\r
350     if(r != EOK){\r
351         ext4_block_fini(bd);\r
352         return r;\r
353     }\r
354 \r
355     bsize = ext4_sb_get_block_size(&mp->fs.sb);\r
356     ext4_block_set_lb_size(bd, bsize);\r
357 \r
358     mp->cache_dynamic = 0;\r
359 \r
360     if(!bc){\r
361         /*Automatic block cache alloc.*/\r
362         mp->cache_dynamic = 1;\r
363         bc = malloc(sizeof(struct ext4_bcache));\r
364 \r
365         r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);\r
366         if(r != EOK){\r
367             free(bc);\r
368             ext4_block_fini(bd);\r
369             return r;\r
370         }\r
371     }\r
372 \r
373     if(bsize != bc->itemsize)\r
374         return ENOTSUP;\r
375 \r
376     /*Bind block cache to block device*/\r
377     r = ext4_block_bind_bcache(bd, bc);\r
378     if(r != EOK){\r
379         ext4_block_fini(bd);\r
380         if(mp->cache_dynamic){\r
381             ext4_bcache_fini_dynamic(bc);\r
382             free(bc);\r
383         }\r
384         return r;\r
385     }\r
386 \r
387     return r;\r
388 }\r
389 \r
390 \r
391 int ext4_umount(char *mount_point)\r
392 {\r
393     int i;\r
394     int r = EOK;\r
395     struct ext4_mountpoint *mp = 0;\r
396 \r
397     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
398         if(_mp[i].name){\r
399             if(!strcmp(_mp[i].name, mount_point))\r
400                 mp = &_mp[i];\r
401             break;\r
402         }\r
403     }\r
404 \r
405     if(!mp)\r
406         return ENODEV;\r
407 \r
408     r = ext4_fs_fini(&mp->fs);\r
409     if(r != EOK)\r
410         return r;\r
411 \r
412     mp->name = 0;\r
413 \r
414     if(mp->cache_dynamic){\r
415         ext4_bcache_fini_dynamic(mp->fs.bdev->bc);\r
416         free(mp->fs.bdev->bc);\r
417     }\r
418 \r
419     return ext4_block_fini(mp->fs.bdev);\r
420 }\r
421 \r
422 int ext4_mount_point_stats(const char *mount_point,\r
423     struct ext4_mount_stats *stats)\r
424 {\r
425     uint32_t i;\r
426     struct ext4_mountpoint    *mp = 0;\r
427 \r
428     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
429         if(_mp[i].name){\r
430             if(!strcmp(_mp[i].name, mount_point))\r
431                 mp = &_mp[i];\r
432             break;\r
433         }\r
434     }\r
435     if(!mp)\r
436         return ENOENT;\r
437 \r
438     EXT4_MP_LOCK(mp);\r
439     stats->inodes_count      = ext4_get32(&mp->fs.sb, inodes_count);\r
440     stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);\r
441     stats->blocks_count      = ext4_sb_get_blocks_cnt(&mp->fs.sb);\r
442     stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);\r
443     stats->block_size        = ext4_sb_get_block_size(&mp->fs.sb);\r
444 \r
445     stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);\r
446     stats->blocks_per_group  = ext4_get32(&mp->fs.sb, blocks_per_group);\r
447     stats->inodes_per_group  = ext4_get32(&mp->fs.sb, inodes_per_group);\r
448 \r
449     memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);\r
450     EXT4_MP_UNLOCK(mp);\r
451 \r
452     return EOK;\r
453 }\r
454 \r
455 /********************************FILE OPERATIONS*****************************/\r
456 \r
457 static struct ext4_mountpoint* ext4_get_mount(const char *path)\r
458 {\r
459     int i;\r
460     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
461         if(_mp[i].name){\r
462             if(!strncmp(_mp[i].name, path, strlen(_mp[i].name)))\r
463                 return &_mp[i];\r
464         }\r
465     }\r
466     return 0;\r
467 }\r
468 \r
469 static int ext4_path_check(const char *path, bool* is_goal)\r
470 {\r
471     int i;\r
472 \r
473     for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {\r
474 \r
475         if(path[i] == '/'){\r
476             *is_goal = false;\r
477             return i;\r
478         }\r
479 \r
480         if(path[i] == 0){\r
481             *is_goal = true;\r
482             return i;\r
483         }\r
484     }\r
485 \r
486     return 0;\r
487 }\r
488 \r
489 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)\r
490 {\r
491     if(!flags)\r
492         return false;\r
493 \r
494     if(!strcmp(flags, "r") || !strcmp(flags, "rb")){\r
495         *file_flags = O_RDONLY;\r
496         return true;\r
497     }\r
498 \r
499     if(!strcmp(flags, "w") || !strcmp(flags, "wb")){\r
500         *file_flags = O_WRONLY | O_CREAT | O_TRUNC;\r
501         return true;\r
502     }\r
503 \r
504     if(!strcmp(flags, "a") || !strcmp(flags, "ab")){\r
505         *file_flags = O_WRONLY | O_CREAT | O_APPEND;\r
506         return true;\r
507     }\r
508 \r
509     if(!strcmp(flags, "r+") || !strcmp(flags, "rb+") || !strcmp(flags, "r+b")){\r
510         *file_flags = O_RDWR;\r
511         return true;\r
512     }\r
513 \r
514     if(!strcmp(flags, "w+") || !strcmp(flags, "wb+") || !strcmp(flags, "w+b")){\r
515         *file_flags = O_RDWR | O_CREAT | O_TRUNC;\r
516         return true;\r
517     }\r
518 \r
519     if(!strcmp(flags, "a+") || !strcmp(flags, "ab+") || !strcmp(flags, "a+b")){\r
520         *file_flags = O_RDWR | O_CREAT | O_APPEND;\r
521         return true;\r
522     }\r
523 \r
524     return false;\r
525 }\r
526 \r
527 /****************************************************************************/\r
528 \r
529 static int ext4_generic_open (ext4_file *f, const char *path,\r
530     const char *flags, bool file_expect, uint32_t *parent_inode, uint32_t *name_off)\r
531 {\r
532     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
533     struct ext4_directory_search_result result;\r
534     struct ext4_inode_ref ref;\r
535     bool is_goal = false;\r
536     uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;\r
537     int r = ENOENT;\r
538     uint32_t next_inode;\r
539 \r
540     f->mp = 0;\r
541 \r
542     if(!mp)\r
543         return ENOENT;\r
544 \r
545     if(ext4_parse_flags(flags, &f->flags) == false)\r
546         return EINVAL;\r
547 \r
548     /*Skip mount point*/\r
549     path += strlen(mp->name);\r
550 \r
551     if(name_off)\r
552         *name_off = strlen(mp->name);\r
553 \r
554     /*Load root*/\r
555     r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);\r
556 \r
557     if(r != EOK)\r
558         return r;\r
559 \r
560     if(parent_inode)\r
561         *parent_inode = ref.index;\r
562 \r
563     int len = ext4_path_check(path, &is_goal);\r
564 \r
565     while(1){\r
566 \r
567         len = ext4_path_check(path, &is_goal);\r
568 \r
569         if(!len){\r
570             /*If root open was request.*/\r
571             if(is_goal && !file_expect)\r
572                 break;\r
573 \r
574             r = ENOENT;\r
575             break;\r
576         }\r
577 \r
578         r = ext4_dir_find_entry(&result, &ref, path, len);\r
579         if(r != EOK){\r
580 \r
581             if(r != ENOENT)\r
582                 break;\r
583 \r
584             if(!(f->flags & O_CREAT))\r
585                 break;\r
586 \r
587             /*O_CREAT allows create new entry*/\r
588             struct ext4_inode_ref child_ref;\r
589             r = ext4_fs_alloc_inode(&mp->fs, &child_ref, is_goal ? !file_expect : true);\r
590             if(r != EOK)\r
591                 break;\r
592 \r
593             /*Destroy last result*/\r
594             ext4_dir_destroy_result(&ref, &result);\r
595 \r
596             /*Link with root dir.*/\r
597             r = ext4_link(mp, &ref, &child_ref, path, len);\r
598             if(r != EOK){\r
599                 /*Fali. Free new inode.*/\r
600                 ext4_fs_free_inode(&child_ref);\r
601                 /*We do not want to write new inode.\r
602                   But block has to be released.*/\r
603                 child_ref.dirty = false;\r
604                 ext4_fs_put_inode_ref(&child_ref);\r
605                 break;\r
606             }\r
607 \r
608             ext4_fs_put_inode_ref(&child_ref);\r
609 \r
610             continue;\r
611         }\r
612 \r
613         if(parent_inode)\r
614             *parent_inode = ref.index;\r
615 \r
616         next_inode = result.dentry->inode;\r
617         inode_type = ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);\r
618 \r
619         r = ext4_dir_destroy_result(&ref, &result);\r
620         if(r != EOK)\r
621             break;\r
622 \r
623         /*If expected file error*/\r
624         if((inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)\r
625                 && !file_expect && is_goal){\r
626             r = ENOENT;\r
627             break;\r
628         }\r
629 \r
630         /*If expected directory error*/\r
631         if((inode_type == EXT4_DIRECTORY_FILETYPE_DIR)\r
632                 && file_expect && is_goal){\r
633             r = ENOENT;\r
634             break;\r
635         }\r
636 \r
637         r = ext4_fs_put_inode_ref(&ref);\r
638         if(r != EOK)\r
639             break;\r
640 \r
641         r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);\r
642         if(r != EOK)\r
643             break;\r
644 \r
645         if(is_goal)\r
646             break;\r
647 \r
648         path += len + 1;\r
649 \r
650         if(name_off)\r
651             *name_off += len + 1;\r
652     };\r
653 \r
654     if(r != EOK){\r
655         ext4_fs_put_inode_ref(&ref);\r
656         return r;\r
657     }\r
658 \r
659     if(is_goal){\r
660 \r
661         if((f->flags & O_TRUNC) &&\r
662                 (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)){\r
663             /*Turncate.*/\r
664             ext4_block_delay_cache_flush(mp->fs.bdev, 1);\r
665             /*Truncate may be IO heavy.\r
666              Do it with delayed cache flush mode.*/\r
667             r = ext4_fs_truncate_inode(&ref, 0);\r
668             ext4_block_delay_cache_flush(mp->fs.bdev, 0);\r
669 \r
670             if(r != EOK){\r
671                 ext4_fs_put_inode_ref(&ref);\r
672                 return r;\r
673             }\r
674         }\r
675 \r
676         f->mp = mp;\r
677         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
678         f->inode = ref.index;\r
679         f->fpos  = 0;\r
680 \r
681         if(f->flags & O_APPEND)\r
682             f->fpos = f->fsize;\r
683     }\r
684 \r
685     r = ext4_fs_put_inode_ref(&ref);\r
686     return r;\r
687 }\r
688 \r
689 /****************************************************************************/\r
690 \r
691 int ext4_fremove(const char *path)\r
692 {\r
693     ext4_file   f;\r
694     uint32_t parent_inode;\r
695     uint32_t name_off;\r
696     int r;\r
697     int len;\r
698     bool is_goal;\r
699     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
700 \r
701     struct ext4_inode_ref child;\r
702     struct ext4_inode_ref parent;\r
703 \r
704     if(!mp)\r
705         return ENOENT;\r
706 \r
707     EXT4_MP_LOCK(mp);\r
708     r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);\r
709     if(r != EOK){\r
710         EXT4_MP_UNLOCK(mp);\r
711         return r;\r
712     }\r
713 \r
714     /*Load parent*/\r
715     r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);\r
716     if(r != EOK){\r
717         EXT4_MP_UNLOCK(mp);\r
718         return r;\r
719     }\r
720 \r
721     /*We have file to delete. Load it.*/\r
722     r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);\r
723     if(r != EOK){\r
724         ext4_fs_put_inode_ref(&parent);\r
725         EXT4_MP_UNLOCK(mp);\r
726         return r;\r
727     }\r
728 \r
729     /*Turncate.*/\r
730     ext4_block_delay_cache_flush(mp->fs.bdev, 1);\r
731     /*Truncate may be IO heavy. Do it with delayed cache flush mode.*/\r
732     r = ext4_fs_truncate_inode(&child, 0);\r
733     ext4_block_delay_cache_flush(mp->fs.bdev, 0);\r
734 \r
735     if(r != EOK)\r
736         goto Finish;\r
737 \r
738     /*Set path*/\r
739     path += name_off;\r
740 \r
741     len = ext4_path_check(path, &is_goal);\r
742 \r
743     /*Unlink from parent.*/\r
744     r = ext4_unlink(mp, &parent, &child, path, len);\r
745     if(r != EOK)\r
746         goto Finish;\r
747 \r
748     r = ext4_fs_free_inode(&child);\r
749     if(r != EOK)\r
750         goto Finish;\r
751 \r
752     Finish:\r
753     ext4_fs_put_inode_ref(&child);\r
754     ext4_fs_put_inode_ref(&parent);\r
755     EXT4_MP_UNLOCK(mp);\r
756     return r;\r
757 }\r
758 \r
759 \r
760 int ext4_fopen (ext4_file *f, const char *path, const char *flags)\r
761 {\r
762     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
763     int r;\r
764 \r
765     if(!mp)\r
766         return ENOENT;\r
767 \r
768     EXT4_MP_LOCK(mp);\r
769     r = ext4_generic_open(f, path, flags, true, 0, 0);\r
770     EXT4_MP_UNLOCK(mp);\r
771     return r;\r
772 }\r
773 \r
774 int ext4_fclose(ext4_file *f)\r
775 {\r
776     ext4_assert(f && f->mp);\r
777 \r
778     f->mp    = 0;\r
779     f->flags = 0;\r
780     f->inode = 0;\r
781     f->fpos  = f->fsize = 0;\r
782 \r
783     return EOK;\r
784 }\r
785 int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)\r
786 {\r
787     int r = EOK;\r
788     uint32_t u;\r
789     uint32_t fblock;\r
790     uint32_t fblock_start;\r
791     uint32_t fblock_cnt;\r
792     struct ext4_block b;\r
793     uint8_t *u8_buf = buf;\r
794     struct ext4_inode_ref ref;\r
795     uint32_t sblock;\r
796     uint32_t sblock_end;\r
797     uint32_t block_size;\r
798 \r
799     ext4_assert(f && f->mp);\r
800 \r
801     if(f->flags & O_WRONLY)\r
802         return EPERM;\r
803 \r
804     if(!size)\r
805         return EOK;\r
806 \r
807     EXT4_MP_LOCK(f->mp);\r
808 \r
809     if(rcnt)\r
810         *rcnt = 0;\r
811 \r
812     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
813     if(r != EOK){\r
814         EXT4_MP_UNLOCK(f->mp);\r
815         return r;\r
816     }\r
817 \r
818     /*Sync file size*/\r
819     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
820 \r
821 \r
822     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
823     size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;\r
824     sblock = (f->fpos) / block_size;\r
825     sblock_end = (f->fpos + size) / block_size;\r
826     u = (f->fpos) % block_size;\r
827 \r
828 \r
829     if(u){\r
830 \r
831         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
832 \r
833         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
834         if(r != EOK)\r
835             goto Finish;\r
836 \r
837         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
838         if(r != EOK)\r
839             goto Finish;\r
840 \r
841         memcpy(u8_buf, b.data + u, ll);\r
842 \r
843         r = ext4_block_set(f->mp->fs.bdev, &b);\r
844         if(r != EOK)\r
845             goto Finish;\r
846 \r
847         u8_buf  += ll;\r
848         size    -= ll;\r
849         f->fpos += ll;\r
850 \r
851         if(rcnt)\r
852             *rcnt += ll;\r
853 \r
854         sblock++;\r
855     }\r
856 \r
857     fblock_start = 0;\r
858     fblock_cnt = 0;\r
859     while(size >= block_size){\r
860         while(sblock < sblock_end){\r
861             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
862             if(r != EOK)\r
863                 goto Finish;\r
864 \r
865             sblock++;\r
866 \r
867             if(!fblock_start){\r
868                 fblock_start = fblock;\r
869             }\r
870 \r
871             if((fblock_start + fblock_cnt) != fblock)\r
872                 break;\r
873 \r
874             fblock_cnt++;\r
875         }\r
876 \r
877         r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start, fblock_cnt);\r
878         if(r != EOK)\r
879             goto Finish;\r
880 \r
881         size    -= block_size * fblock_cnt;\r
882         u8_buf  += block_size * fblock_cnt;\r
883         f->fpos += block_size * fblock_cnt;\r
884 \r
885         if(rcnt)\r
886             *rcnt += block_size * fblock_cnt;\r
887 \r
888         fblock_start = fblock;\r
889         fblock_cnt = 1;\r
890     }\r
891 \r
892     if(size){\r
893         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
894         if(r != EOK)\r
895             goto Finish;\r
896 \r
897         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
898         if(r != EOK)\r
899             goto Finish;\r
900 \r
901         memcpy(u8_buf, b.data , size);\r
902 \r
903         r = ext4_block_set(f->mp->fs.bdev, &b);\r
904         if(r != EOK)\r
905             goto Finish;\r
906 \r
907         f->fpos += size;\r
908 \r
909         if(rcnt)\r
910             *rcnt += size;\r
911     }\r
912 \r
913     Finish:\r
914     ext4_fs_put_inode_ref(&ref);\r
915     EXT4_MP_UNLOCK(f->mp);\r
916     return r;\r
917 }\r
918 \r
919 int ext4_fwrite(ext4_file *f, void *buf, uint32_t size, uint32_t *wcnt)\r
920 {\r
921     int r = EOK;\r
922     uint32_t u;\r
923     uint32_t fblock;\r
924     struct ext4_block b;\r
925     uint8_t *u8_buf = buf;\r
926     struct ext4_inode_ref ref;\r
927     uint32_t sblock;\r
928     uint32_t sblock_end;\r
929     uint32_t file_blocks;\r
930     uint32_t block_size;\r
931     uint32_t fblock_start;\r
932     uint32_t fblock_cnt;\r
933 \r
934     ext4_assert(f && f->mp);\r
935 \r
936     if(f->flags & O_RDONLY)\r
937         return EPERM;\r
938 \r
939     if(!size)\r
940         return EOK;\r
941 \r
942     EXT4_MP_LOCK(f->mp);\r
943 \r
944     if(wcnt)\r
945         *wcnt = 0;\r
946 \r
947     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
948     if(r != EOK){\r
949         EXT4_MP_UNLOCK(f->mp);\r
950         return r;\r
951     }\r
952 \r
953     /*Sync file size*/\r
954     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
955 \r
956     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
957 \r
958     sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;\r
959     sblock_end /= block_size;\r
960     file_blocks = (f->fsize / block_size);\r
961 \r
962     if(f->fsize % block_size)\r
963         file_blocks++;\r
964 \r
965     sblock = (f->fpos) / block_size;\r
966 \r
967     u = (f->fpos) % block_size;\r
968 \r
969 \r
970     if(u){\r
971         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
972 \r
973         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
974         if(r != EOK)\r
975             goto Finish;\r
976 \r
977         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
978         if(r != EOK)\r
979             goto Finish;\r
980 \r
981         memcpy(b.data + u, u8_buf, ll);\r
982         b.dirty = true;\r
983 \r
984         r = ext4_block_set(f->mp->fs.bdev, &b);\r
985         if(r != EOK)\r
986             goto Finish;\r
987 \r
988         u8_buf  += ll;\r
989         size    -= ll;\r
990         f->fpos += ll;\r
991 \r
992         if(wcnt)\r
993             *wcnt += ll;\r
994 \r
995         sblock++;\r
996     }\r
997 \r
998 \r
999     /*Start delay cache flush mode.*/\r
1000     r = ext4_block_delay_cache_flush(f->mp->fs.bdev, 1);\r
1001     if(r != EOK)\r
1002         goto Finish;\r
1003 \r
1004     fblock_start = 0;\r
1005     fblock_cnt = 0;\r
1006     while(size >= block_size){\r
1007 \r
1008         while(sblock < sblock_end){\r
1009             if(sblock < file_blocks){\r
1010                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1011                 if(r != EOK)\r
1012                     break;\r
1013             }\r
1014             else {\r
1015                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1016                 if(r != EOK)\r
1017                     break;\r
1018             }\r
1019 \r
1020             sblock++;\r
1021 \r
1022             if(!fblock_start){\r
1023                 fblock_start = fblock;\r
1024             }\r
1025 \r
1026             if((fblock_start + fblock_cnt) != fblock)\r
1027                 break;\r
1028 \r
1029             fblock_cnt++;\r
1030         }\r
1031 \r
1032         r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start, fblock_cnt);\r
1033         if(r != EOK)\r
1034             break;\r
1035 \r
1036         size    -= block_size * fblock_cnt;\r
1037         u8_buf  += block_size * fblock_cnt;\r
1038         f->fpos += block_size * fblock_cnt;\r
1039 \r
1040         if(wcnt)\r
1041             *wcnt += block_size * fblock_cnt;\r
1042 \r
1043         fblock_start = fblock;\r
1044         fblock_cnt = 1;\r
1045     }\r
1046 \r
1047     /*Stop delay cache flush mode*/\r
1048     ext4_block_delay_cache_flush(f->mp->fs.bdev, 0);\r
1049 \r
1050     if(r != EOK)\r
1051         goto Finish;\r
1052 \r
1053     if(size){\r
1054         if(sblock < file_blocks){\r
1055             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1056             if(r != EOK)\r
1057                 goto Finish;\r
1058         }\r
1059         else {\r
1060             r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1061             if(r != EOK)\r
1062                 goto Finish;\r
1063         }\r
1064 \r
1065         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1066         if(r != EOK)\r
1067             goto Finish;\r
1068 \r
1069         memcpy(b.data, u8_buf , size);\r
1070         b.dirty = true;\r
1071 \r
1072         r = ext4_block_set(f->mp->fs.bdev, &b);\r
1073         if(r != EOK)\r
1074             goto Finish;\r
1075 \r
1076         f->fpos += size;\r
1077 \r
1078         if(wcnt)\r
1079             *wcnt += size;\r
1080     }\r
1081 \r
1082     if(f->fpos > f->fsize){\r
1083         f->fsize = f->fpos;\r
1084         ext4_inode_set_size(ref.inode, f->fsize);\r
1085         ref.dirty = true;\r
1086     }\r
1087 \r
1088     Finish:\r
1089     ext4_fs_put_inode_ref(&ref);\r
1090     EXT4_MP_UNLOCK(f->mp);\r
1091     return r;\r
1092 \r
1093 }\r
1094 \r
1095 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)\r
1096 {\r
1097     switch(origin){\r
1098     case SEEK_SET:\r
1099         if(offset > f->fsize)\r
1100             return EINVAL;\r
1101 \r
1102         f->fpos = offset;\r
1103         return EOK;\r
1104     case SEEK_CUR:\r
1105         if((offset + f->fpos) > f->fsize)\r
1106             return EINVAL;\r
1107 \r
1108         f->fpos += offset;\r
1109         return EOK;\r
1110     case SEEK_END:\r
1111         if(offset > f->fsize)\r
1112             return EINVAL;\r
1113 \r
1114         f->fpos = f->fsize - offset;\r
1115         return EOK;\r
1116 \r
1117     }\r
1118     return EINVAL;\r
1119 }\r
1120 \r
1121 uint64_t ext4_ftell (ext4_file *f)\r
1122 {\r
1123     return f->fpos;\r
1124 }\r
1125 \r
1126 uint64_t ext4_fsize (ext4_file *f)\r
1127 {\r
1128     return f->fsize;\r
1129 }\r
1130 \r
1131 /*********************************DIRECTORY OPERATION************************/\r
1132 \r
1133 int ext4_dir_rm(const char *path)\r
1134 {\r
1135     int r;\r
1136     int len;\r
1137     ext4_file f;\r
1138 \r
1139     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1140     struct ext4_inode_ref current;\r
1141     struct ext4_inode_ref child;\r
1142     struct ext4_directory_iterator it;\r
1143 \r
1144     uint32_t name_off;\r
1145     uint32_t inode_up;\r
1146     uint32_t inode_current;\r
1147     uint32_t depth = 1;\r
1148 \r
1149     bool has_children;\r
1150     bool is_goal;\r
1151     bool dir_end;\r
1152 \r
1153     if(!mp)\r
1154         return ENOENT;\r
1155 \r
1156     EXT4_MP_LOCK(mp);\r
1157 \r
1158     /*Check if exist.*/\r
1159     r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);\r
1160     if(r != EOK){\r
1161         EXT4_MP_UNLOCK(mp);\r
1162         return r;\r
1163     }\r
1164 \r
1165     path += name_off;\r
1166     len = ext4_path_check(path, &is_goal);\r
1167 \r
1168     inode_current = f.inode;\r
1169     dir_end = false;\r
1170 \r
1171     ext4_block_delay_cache_flush(mp->fs.bdev, 1);\r
1172 \r
1173     do {\r
1174         /*Load directory node.*/\r
1175         r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);\r
1176         if(r != EOK){\r
1177             break;\r
1178         }\r
1179 \r
1180         /*Initialize iterator.*/\r
1181         r = ext4_dir_iterator_init(&it, &current, 0);\r
1182         if(r != EOK){\r
1183             ext4_fs_put_inode_ref(&current);\r
1184             break;\r
1185         }\r
1186 \r
1187         while(r == EOK){\r
1188 \r
1189             if(!it.current){\r
1190                 dir_end = true;\r
1191                 break;\r
1192             }\r
1193 \r
1194             /*Get up directory inode when ".." entry*/\r
1195             if((it.current->name_length == 2) &&\r
1196                     ext4_is_dots(it.current->name, it.current->name_length)){\r
1197                 inode_up = it.current->inode;\r
1198             }\r
1199 \r
1200             /*If directory or file entry,  but not "." ".." entry*/\r
1201             if(!ext4_is_dots(it.current->name, it.current->name_length)){\r
1202 \r
1203                 /*Get child inode reference do unlink directory/file.*/\r
1204                 r = ext4_fs_get_inode_ref(&f.mp->fs, it.current->inode, &child);\r
1205                 if(r != EOK)\r
1206                     break;\r
1207 \r
1208                 /*If directory with no leaf children*/\r
1209                 r = ext4_has_children(&has_children, &child);\r
1210                 if(r != EOK){\r
1211                     ext4_fs_put_inode_ref(&child);\r
1212                     break;\r
1213                 }\r
1214 \r
1215                 if(has_children){\r
1216                     /*Has directory children. Go into this tirectory.*/\r
1217                     inode_up = inode_current;\r
1218                     inode_current = it.current->inode;\r
1219                     depth++;\r
1220                     ext4_fs_put_inode_ref(&child);\r
1221                     break;\r
1222                 }\r
1223 \r
1224                 /*Directory is empty. Truncate it.*/\r
1225                 r = ext4_fs_truncate_inode(&child, 0);\r
1226                 if(r != EOK){\r
1227                     ext4_fs_put_inode_ref(&child);\r
1228                     break;\r
1229                 }\r
1230 \r
1231                 /*No children in child directory or file. Just unlink.*/\r
1232                 r = ext4_unlink(f.mp, &current, &child,\r
1233                         (char *)it.current->name, it.current->name_length);\r
1234                 if(r != EOK){\r
1235                     ext4_fs_put_inode_ref(&child);\r
1236                     break;\r
1237                 }\r
1238 \r
1239                 r = ext4_fs_free_inode(&child);\r
1240                 if(r != EOK){\r
1241                     ext4_fs_put_inode_ref(&child);\r
1242                     break;\r
1243                 }\r
1244 \r
1245                 r = ext4_fs_put_inode_ref(&child);\r
1246                 if(r != EOK)\r
1247                     break;\r
1248             }\r
1249 \r
1250             r = ext4_dir_iterator_next(&it);\r
1251         }\r
1252 \r
1253         if(dir_end){\r
1254             /*Directory iterator reached last entry*/\r
1255             ext4_has_children(&has_children, &current);\r
1256             if(!has_children){\r
1257                 inode_current = inode_up;\r
1258                 if(depth)\r
1259                     depth--;\r
1260             }\r
1261             /*Last unlink*/\r
1262             if(!depth){\r
1263                 /*Load parent.*/\r
1264                 struct ext4_inode_ref parent;\r
1265                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up, &parent);\r
1266                 if(r != EOK)\r
1267                     goto End;\r
1268 \r
1269                 r = ext4_fs_truncate_inode(&current, 0);\r
1270                 if(r != EOK){\r
1271                     ext4_fs_put_inode_ref(&parent);\r
1272                     goto End;\r
1273                 }\r
1274 \r
1275                 /* In this place all directories should be unlinked.\r
1276                  * Last unlink from root of current directory*/\r
1277                 r = ext4_unlink(f.mp, &parent, &current, (char *)path, len);\r
1278                 if(r != EOK){\r
1279                     ext4_fs_put_inode_ref(&parent);\r
1280                     goto End;\r
1281                 }\r
1282 \r
1283                 r = ext4_fs_free_inode(&current);\r
1284                 if(r != EOK){\r
1285                     ext4_fs_put_inode_ref(&parent);\r
1286                     goto End;\r
1287                 }\r
1288 \r
1289                 r = ext4_fs_put_inode_ref(&parent);\r
1290                 if(r != EOK)\r
1291                     goto End;\r
1292             }\r
1293         }\r
1294 \r
1295         End:\r
1296         ext4_dir_iterator_fini(&it);\r
1297         ext4_fs_put_inode_ref(&current);\r
1298         dir_end = false;\r
1299 \r
1300         /*When something goes wrong. End loop.*/\r
1301         if(r != EOK)\r
1302             break;\r
1303 \r
1304     }while(depth);\r
1305 \r
1306     ext4_block_delay_cache_flush(mp->fs.bdev, 0);\r
1307     EXT4_MP_UNLOCK(mp);\r
1308     return r;\r
1309 }\r
1310 \r
1311 int ext4_dir_mk(const char *path)\r
1312 {\r
1313     int r;\r
1314     ext4_file f;\r
1315 \r
1316     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1317 \r
1318     if(!mp)\r
1319         return ENOENT;\r
1320 \r
1321     EXT4_MP_LOCK(mp);\r
1322 \r
1323     /*Check if exist.*/\r
1324     r = ext4_generic_open(&f, path, "r", false, 0, 0);\r
1325     if(r == EOK){\r
1326         /*Directory already created*/\r
1327         EXT4_MP_UNLOCK(mp);\r
1328         return r;\r
1329     }\r
1330 \r
1331     /*Create new dir*/\r
1332     r = ext4_generic_open(&f, path, "w", false, 0, 0);\r
1333     if(r != EOK){\r
1334         EXT4_MP_UNLOCK(mp);\r
1335         return r;\r
1336     }\r
1337 \r
1338     EXT4_MP_UNLOCK(mp);\r
1339     return r;\r
1340 }\r
1341 \r
1342 int ext4_dir_open (ext4_dir *d, const char *path)\r
1343 {\r
1344     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1345     int r;\r
1346 \r
1347     if(!mp)\r
1348         return ENOENT;\r
1349 \r
1350     EXT4_MP_LOCK(mp);\r
1351     r = ext4_generic_open(&d->f, path, "r", false, 0, 0);\r
1352     EXT4_MP_UNLOCK(mp);\r
1353     return r;\r
1354 }\r
1355 \r
1356 int ext4_dir_close(ext4_dir *d)\r
1357 {\r
1358     return ext4_fclose(&d->f);\r
1359 }\r
1360 \r
1361 ext4_direntry* ext4_dir_entry_get(ext4_dir *d, uint32_t id)\r
1362 {\r
1363     int r;\r
1364     uint32_t i;\r
1365     ext4_direntry *de = 0;\r
1366     struct ext4_inode_ref dir;\r
1367     struct ext4_directory_iterator it;\r
1368 \r
1369     EXT4_MP_LOCK(d->f.mp);\r
1370 \r
1371     r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);\r
1372     if(r != EOK){\r
1373         goto Finish;\r
1374     }\r
1375 \r
1376     r = ext4_dir_iterator_init(&it, &dir, 0);\r
1377     if(r != EOK){\r
1378         ext4_fs_put_inode_ref(&dir);\r
1379         goto Finish;\r
1380     }\r
1381 \r
1382     i = 0;\r
1383     while(r == EOK){\r
1384 \r
1385         if(!it.current)\r
1386             break;\r
1387 \r
1388         if(i == id){\r
1389             memcpy(&d->de, it.current, sizeof(ext4_direntry));\r
1390             de = &d->de;\r
1391             break;\r
1392         }\r
1393 \r
1394         i++;\r
1395         r = ext4_dir_iterator_next(&it);\r
1396     }\r
1397 \r
1398     ext4_dir_iterator_fini(&it);\r
1399     ext4_fs_put_inode_ref(&dir);\r
1400 \r
1401     Finish:\r
1402     EXT4_MP_UNLOCK(d->f.mp);\r
1403     return de;\r
1404 }\r
1405 \r
1406 /**\r
1407  * @}\r
1408  */\r