Add readme file.
[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 \r
359     mp->cache_dynamic = 0;\r
360 \r
361     if(!bc){\r
362         /*Automatic block cache alloc.*/\r
363         mp->cache_dynamic = 1;\r
364         bc = malloc(sizeof(struct ext4_bcache));\r
365 \r
366         r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);\r
367         if(r != EOK){\r
368             free(bc);\r
369             ext4_block_fini(bd);\r
370             return r;\r
371         }\r
372     }\r
373 \r
374     if(bsize != bc->itemsize)\r
375         return ENOTSUP;\r
376 \r
377 \r
378     /*Bind block cache to block device*/\r
379     r = ext4_block_bind_bcache(bd, bc);\r
380     if(r != EOK){\r
381         ext4_block_fini(bd);\r
382         if(mp->cache_dynamic){\r
383             ext4_bcache_fini_dynamic(bc);\r
384             free(bc);\r
385         }\r
386         return r;\r
387     }\r
388 \r
389     return r;\r
390 }\r
391 \r
392 \r
393 int                     ext4_umount(char *mount_point)\r
394 {\r
395     int         i;\r
396     int         r = EOK;\r
397     struct ext4_mountpoint      *mp = 0;\r
398 \r
399     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
400         if(_mp[i].name){\r
401             if(!strcmp(_mp[i].name, mount_point))\r
402                 mp = &_mp[i];\r
403             break;\r
404         }\r
405     }\r
406 \r
407     if(!mp)\r
408         return ENODEV;\r
409 \r
410     r = ext4_fs_fini(&mp->fs);\r
411     if(r != EOK)\r
412         return r;\r
413 \r
414     mp->name = 0;\r
415 \r
416     if(mp->cache_dynamic){\r
417         ext4_bcache_fini_dynamic(mp->fs.bdev->bc);\r
418         free(mp->fs.bdev->bc);\r
419     }\r
420 \r
421     return ext4_block_fini(mp->fs.bdev);\r
422 }\r
423 \r
424 int ext4_mount_point_stats(const char *mount_point,\r
425     struct ext4_mount_stats *stats)\r
426 {\r
427     uint32_t i;\r
428     struct ext4_mountpoint    *mp = 0;\r
429 \r
430     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
431         if(_mp[i].name){\r
432             if(!strcmp(_mp[i].name, mount_point))\r
433                 mp = &_mp[i];\r
434             break;\r
435         }\r
436     }\r
437 \r
438     if(!mp)\r
439         return ENOENT;\r
440 \r
441 \r
442     EXT4_MP_LOCK(mp);\r
443     stats->inodes_count      = ext4_get32(&mp->fs.sb, inodes_count);\r
444     stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);\r
445     stats->blocks_count      = ext4_sb_get_blocks_cnt(&mp->fs.sb);\r
446     stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);\r
447     stats->block_size        = ext4_sb_get_block_size(&mp->fs.sb);\r
448 \r
449     stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);\r
450     stats->blocks_per_group  = ext4_get32(&mp->fs.sb, blocks_per_group);\r
451     stats->inodes_per_group  = ext4_get32(&mp->fs.sb, inodes_per_group);\r
452 \r
453     memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);\r
454     EXT4_MP_UNLOCK(mp);\r
455 \r
456     return EOK;\r
457 }\r
458 \r
459 /********************************FILE OPERATIONS*****************************/\r
460 \r
461 static struct ext4_mountpoint*  ext4_get_mount(const char *path)\r
462 {\r
463     int i;\r
464     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
465         if(_mp[i].name){\r
466             if(!strncmp(_mp[i].name, path, strlen(_mp[i].name)))\r
467                 return &_mp[i];\r
468         }\r
469     }\r
470     return 0;\r
471 }\r
472 \r
473 \r
474 static int ext4_path_check(const char *path, bool* is_goal)\r
475 {\r
476     int i;\r
477 \r
478     for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {\r
479 \r
480         if(path[i] == '/'){\r
481             *is_goal = false;\r
482             return i;\r
483         }\r
484 \r
485         if(path[i] == 0){\r
486             *is_goal = true;\r
487             return i;\r
488         }\r
489     }\r
490 \r
491     return 0;\r
492 }\r
493 \r
494 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)\r
495 {\r
496     if(!flags)\r
497         return false;\r
498 \r
499     if(!strcmp(flags, "r") || !strcmp(flags, "rb")){\r
500         *file_flags = O_RDONLY;\r
501         return true;\r
502     }\r
503 \r
504     if(!strcmp(flags, "w") || !strcmp(flags, "wb")){\r
505         *file_flags = O_WRONLY | O_CREAT | O_TRUNC;\r
506         return true;\r
507     }\r
508 \r
509     if(!strcmp(flags, "a") || !strcmp(flags, "ab")){\r
510         *file_flags = O_WRONLY | O_CREAT | O_APPEND     ;\r
511         return true;\r
512     }\r
513 \r
514     if(!strcmp(flags, "r+") || !strcmp(flags, "rb+") || !strcmp(flags, "r+b")){\r
515         *file_flags = O_RDWR;\r
516         return true;\r
517     }\r
518 \r
519     if(!strcmp(flags, "w+") || !strcmp(flags, "wb+") || !strcmp(flags, "w+b")){\r
520         *file_flags = O_RDWR | O_CREAT | O_TRUNC;\r
521         return true;\r
522     }\r
523 \r
524     if(!strcmp(flags, "a+") || !strcmp(flags, "ab+") || !strcmp(flags, "a+b")){\r
525         *file_flags = O_RDWR | O_CREAT | O_APPEND;\r
526         return true;\r
527     }\r
528 \r
529     return false;\r
530 }\r
531 \r
532 /****************************************************************************/\r
533 \r
534 static int ext4_generic_open (ext4_file *f, const char *path,\r
535         const char *flags, bool file_expect, uint32_t *parent_inode, uint32_t *name_off)\r
536 {\r
537     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
538     struct ext4_directory_search_result result;\r
539     struct ext4_inode_ref       ref;\r
540     bool        is_goal = false;\r
541     uint8_t     inode_type;\r
542     int         r = ENOENT;\r
543     uint32_t next_inode;\r
544 \r
545     f->mp = 0;\r
546 \r
547     if(!mp)\r
548         return ENOENT;\r
549 \r
550     if(ext4_parse_flags(flags, &f->flags) == false)\r
551         return EINVAL;\r
552 \r
553     /*Skip mount point*/\r
554     path += strlen(mp->name);\r
555 \r
556     if(name_off)\r
557         *name_off = strlen(mp->name);\r
558 \r
559     /*Load root*/\r
560     r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);\r
561 \r
562     if(r != EOK)\r
563         return r;\r
564 \r
565     if(parent_inode)\r
566         *parent_inode = ref.index;\r
567 \r
568     int len = ext4_path_check(path, &is_goal);\r
569 \r
570     while(1){\r
571 \r
572         len = ext4_path_check(path, &is_goal);\r
573 \r
574         if(!len){\r
575             /*If root open was request.*/\r
576             if(is_goal && !file_expect)\r
577                 break;\r
578 \r
579             r = ENOENT;\r
580             break;\r
581         }\r
582 \r
583         r = ext4_dir_find_entry(&result, &ref, path, len);\r
584         if(r != EOK){\r
585 \r
586             if(r != ENOENT)\r
587                 break;\r
588 \r
589             if(!(f->flags & O_CREAT))\r
590                 break;\r
591 \r
592             /*O_CREAT allows create new entry*/\r
593             struct ext4_inode_ref       child_ref;\r
594             r = ext4_fs_alloc_inode(&mp->fs, &child_ref, is_goal ? !file_expect : true);\r
595             if(r != EOK)\r
596                 break;\r
597 \r
598             /*Destroy last result*/\r
599             ext4_dir_destroy_result(&ref, &result);\r
600 \r
601             /*Link with root dir.*/\r
602             r = ext4_link(mp, &ref, &child_ref, path, len);\r
603             if(r != EOK){\r
604                 /*Fali. Free new inode.*/\r
605                 ext4_fs_free_inode(&child_ref);\r
606                 /*We do not want to write new inode.\r
607                   But block has to be released.*/\r
608                 child_ref.dirty = false;\r
609                 ext4_fs_put_inode_ref(&child_ref);\r
610                 break;\r
611             }\r
612 \r
613             ext4_fs_put_inode_ref(&child_ref);\r
614 \r
615             continue;\r
616         }\r
617 \r
618         if(parent_inode)\r
619             *parent_inode = ref.index;\r
620 \r
621         next_inode = result.dentry->inode;\r
622         inode_type = ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);\r
623 \r
624         r = ext4_dir_destroy_result(&ref, &result);\r
625         if(r != EOK)\r
626             break;\r
627 \r
628         /*If expected file error*/\r
629         if((inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)\r
630                 && !file_expect && is_goal){\r
631             r = ENOENT;\r
632             break;\r
633         }\r
634 \r
635         /*If expected directory error*/\r
636         if((inode_type == EXT4_DIRECTORY_FILETYPE_DIR)\r
637                 && file_expect && is_goal){\r
638             r = ENOENT;\r
639             break;\r
640         }\r
641 \r
642         r = ext4_fs_put_inode_ref(&ref);\r
643         if(r != EOK)\r
644             break;\r
645 \r
646         r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);\r
647         if(r != EOK)\r
648             break;\r
649 \r
650         if(is_goal)\r
651             break;\r
652 \r
653         path += len + 1;\r
654 \r
655         if(name_off)\r
656             *name_off += len + 1;\r
657     };\r
658 \r
659     if(r != EOK){\r
660         ext4_fs_put_inode_ref(&ref);\r
661         return r;\r
662     }\r
663 \r
664     if(is_goal){\r
665 \r
666         if((f->flags & O_TRUNC) &&\r
667                 (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)){\r
668             /*Turncate.*/\r
669             ext4_block_delay_cache_flush(mp->fs.bdev, 1);\r
670             /*Truncate may be IO heavy.\r
671              Do it with delayed cache flush mode.*/\r
672             r = ext4_fs_truncate_inode(&ref, 0);\r
673             ext4_block_delay_cache_flush(mp->fs.bdev, 0);\r
674 \r
675             if(r != EOK){\r
676                 ext4_fs_put_inode_ref(&ref);\r
677                 return r;\r
678             }\r
679         }\r
680 \r
681         f->mp = mp;\r
682         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
683         f->inode = ref.index;\r
684         f->fpos  = 0;\r
685 \r
686         if(f->flags & O_APPEND)\r
687             f->fpos = f->fsize;\r
688 \r
689     }\r
690 \r
691     r = ext4_fs_put_inode_ref(&ref);\r
692     return r;\r
693 }\r
694 \r
695 /****************************************************************************/\r
696 \r
697 int ext4_fremove(const char *path)\r
698 {\r
699     ext4_file   f;\r
700     uint32_t    parent_inode;\r
701     uint32_t    name_off;\r
702     int     r;\r
703     int     len;\r
704     bool    is_goal;\r
705     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
706 \r
707     struct ext4_inode_ref child;\r
708     struct ext4_inode_ref parent;\r
709 \r
710     if(!mp)\r
711         return ENOENT;\r
712 \r
713     EXT4_MP_LOCK(mp);\r
714     r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);\r
715     if(r != EOK){\r
716         EXT4_MP_UNLOCK(mp);\r
717         return r;\r
718     }\r
719 \r
720     /*Load parent*/\r
721     r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);\r
722     if(r != EOK){\r
723         EXT4_MP_UNLOCK(mp);\r
724         return r;\r
725     }\r
726 \r
727     /*We have file to delete. Load it.*/\r
728     r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);\r
729     if(r != EOK){\r
730         ext4_fs_put_inode_ref(&parent);\r
731         EXT4_MP_UNLOCK(mp);\r
732         return r;\r
733     }\r
734 \r
735     /*Turncate.*/\r
736     ext4_block_delay_cache_flush(mp->fs.bdev, 1);\r
737     /*Truncate may be IO heavy. Do it with delayed cache flush mode.*/\r
738     r = ext4_fs_truncate_inode(&child, 0);\r
739     ext4_block_delay_cache_flush(mp->fs.bdev, 0);\r
740 \r
741     if(r != EOK)\r
742         goto Finish;\r
743 \r
744     /*Set path*/\r
745     path += name_off;\r
746 \r
747     len = ext4_path_check(path, &is_goal);\r
748 \r
749     /*Unlink from parent.*/\r
750     r = ext4_unlink(mp, &parent, &child, path, len);\r
751     if(r != EOK)\r
752         goto Finish;\r
753 \r
754     r = ext4_fs_free_inode(&child);\r
755     if(r != EOK)\r
756         goto Finish;\r
757 \r
758     Finish:\r
759     ext4_fs_put_inode_ref(&child);\r
760     ext4_fs_put_inode_ref(&parent);\r
761     EXT4_MP_UNLOCK(mp);\r
762     return r;\r
763 }\r
764 \r
765 \r
766 int     ext4_fopen (ext4_file *f, const char *path, const char *flags)\r
767 {\r
768     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
769     int r;\r
770 \r
771     if(!mp)\r
772         return ENOENT;\r
773 \r
774     EXT4_MP_LOCK(mp);\r
775     r = ext4_generic_open(f, path, flags, true, 0, 0);\r
776     EXT4_MP_UNLOCK(mp);\r
777     return r;\r
778 }\r
779 \r
780 int     ext4_fclose(ext4_file *f)\r
781 {\r
782     ext4_assert(f && f->mp);\r
783 \r
784     f->mp    = 0;\r
785     f->flags = 0;\r
786     f->inode = 0;\r
787     f->fpos  = f->fsize = 0;\r
788 \r
789     return EOK;\r
790 }\r
791 int     ext4_fread (ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)\r
792 {\r
793     int          r = EOK;\r
794     uint32_t u;\r
795     uint32_t fblock;\r
796     uint32_t fblock_start;\r
797     uint32_t fblock_cnt;\r
798     struct ext4_block b;\r
799     uint8_t     *u8_buf = buf;\r
800     struct ext4_inode_ref ref;\r
801     uint32_t sblock;\r
802     uint32_t sblock_end;\r
803     uint32_t block_size;\r
804 \r
805     ext4_assert(f && f->mp);\r
806 \r
807     if(f->flags & O_WRONLY)\r
808         return EPERM;\r
809 \r
810     if(!size)\r
811         return EOK;\r
812 \r
813     EXT4_MP_LOCK(f->mp);\r
814 \r
815     if(rcnt)\r
816         *rcnt = 0;\r
817 \r
818     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
819     if(r != EOK){\r
820         EXT4_MP_UNLOCK(f->mp);\r
821         return r;\r
822     }\r
823 \r
824     /*Sync file size*/\r
825     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
826 \r
827 \r
828     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
829     size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;\r
830     sblock      = (f->fpos) / block_size;\r
831     sblock_end = (f->fpos + size) / block_size;\r
832     u = (f->fpos) % block_size;\r
833 \r
834 \r
835     if(u){\r
836 \r
837         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
838 \r
839         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
840         if(r != EOK)\r
841             goto Finish;\r
842 \r
843         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
844         if(r != EOK)\r
845             goto Finish;\r
846 \r
847         memcpy(u8_buf, b.data + u, ll);\r
848 \r
849         r = ext4_block_set(f->mp->fs.bdev, &b);\r
850         if(r != EOK)\r
851             goto Finish;\r
852 \r
853         u8_buf  += ll;\r
854         size    -= ll;\r
855         f->fpos += ll;\r
856 \r
857         if(rcnt)\r
858             *rcnt += ll;\r
859 \r
860         sblock++;\r
861     }\r
862 \r
863     fblock_start = 0;\r
864     fblock_cnt = 0;\r
865     while(size >= block_size){\r
866         while(sblock < sblock_end){\r
867             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
868             if(r != EOK)\r
869                 goto Finish;\r
870 \r
871             sblock++;\r
872 \r
873             if(!fblock_start){\r
874                 fblock_start = fblock;\r
875             }\r
876 \r
877             if((fblock_start + fblock_cnt) != fblock)\r
878                 break;\r
879 \r
880             fblock_cnt++;\r
881         }\r
882 \r
883         r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start, fblock_cnt);\r
884         if(r != EOK)\r
885             goto Finish;\r
886 \r
887         size    -= block_size * fblock_cnt;\r
888         u8_buf  += block_size * fblock_cnt;\r
889         f->fpos += block_size * fblock_cnt;\r
890 \r
891         if(rcnt)\r
892             *rcnt += block_size * fblock_cnt;\r
893 \r
894         fblock_start = fblock;\r
895         fblock_cnt = 1;\r
896     }\r
897 \r
898     if(size){\r
899         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
900         if(r != EOK)\r
901             goto Finish;\r
902 \r
903         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
904         if(r != EOK)\r
905             goto Finish;\r
906 \r
907         memcpy(u8_buf, b.data , size);\r
908 \r
909         r = ext4_block_set(f->mp->fs.bdev, &b);\r
910         if(r != EOK)\r
911             goto Finish;\r
912 \r
913         f->fpos += size;\r
914 \r
915         if(rcnt)\r
916             *rcnt += size;\r
917     }\r
918 \r
919     Finish:\r
920     ext4_fs_put_inode_ref(&ref);\r
921     EXT4_MP_UNLOCK(f->mp);\r
922     return r;\r
923 }\r
924 \r
925 int     ext4_fwrite(ext4_file *f, void *buf, uint32_t size, uint32_t *wcnt)\r
926 {\r
927     int          r = EOK;\r
928     uint32_t u;\r
929     uint32_t fblock;\r
930     struct ext4_block b;\r
931     uint8_t     *u8_buf = buf;\r
932     struct ext4_inode_ref ref;\r
933     uint32_t sblock;\r
934     uint32_t sblock_end;\r
935     uint32_t file_blocks;\r
936     uint32_t block_size;\r
937     uint32_t fblock_start;\r
938     uint32_t fblock_cnt;\r
939 \r
940     ext4_assert(f && f->mp);\r
941 \r
942     if(f->flags & O_RDONLY)\r
943         return EPERM;\r
944 \r
945     if(!size)\r
946         return EOK;\r
947 \r
948     EXT4_MP_LOCK(f->mp);\r
949 \r
950     if(wcnt)\r
951         *wcnt = 0;\r
952 \r
953     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
954     if(r != EOK){\r
955         EXT4_MP_UNLOCK(f->mp);\r
956         return r;\r
957     }\r
958 \r
959     /*Sync file size*/\r
960     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
961 \r
962     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
963 \r
964     sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;\r
965     sblock_end /= block_size;\r
966     file_blocks = (f->fsize / block_size);\r
967 \r
968     if(f->fsize % block_size)\r
969         file_blocks++;\r
970 \r
971     sblock      = (f->fpos) / block_size;\r
972 \r
973     u = (f->fpos) % block_size;\r
974 \r
975 \r
976     if(u){\r
977         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
978 \r
979         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
980         if(r != EOK)\r
981             goto Finish;\r
982 \r
983         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
984         if(r != EOK)\r
985             goto Finish;\r
986 \r
987         memcpy(b.data + u, u8_buf, ll);\r
988         b.dirty = true;\r
989 \r
990         r = ext4_block_set(f->mp->fs.bdev, &b);\r
991         if(r != EOK)\r
992             goto Finish;\r
993 \r
994         u8_buf  += ll;\r
995         size    -= ll;\r
996         f->fpos += ll;\r
997 \r
998         if(wcnt)\r
999             *wcnt += ll;\r
1000 \r
1001         sblock++;\r
1002     }\r
1003 \r
1004 \r
1005     /*Start delay cache flush mode.*/\r
1006     r = ext4_block_delay_cache_flush(f->mp->fs.bdev, 1);\r
1007     if(r != EOK)\r
1008         goto Finish;\r
1009 \r
1010     fblock_start = 0;\r
1011     fblock_cnt = 0;\r
1012     while(size >= block_size){\r
1013 \r
1014         while(sblock < sblock_end){\r
1015             if(sblock < file_blocks){\r
1016                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1017                 if(r != EOK)\r
1018                     break;\r
1019             }\r
1020             else {\r
1021                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1022                 if(r != EOK)\r
1023                     break;\r
1024             }\r
1025 \r
1026             sblock++;\r
1027 \r
1028             if(!fblock_start){\r
1029                 fblock_start = fblock;\r
1030             }\r
1031 \r
1032             if((fblock_start + fblock_cnt) != fblock)\r
1033                 break;\r
1034 \r
1035             fblock_cnt++;\r
1036         }\r
1037 \r
1038         r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start, fblock_cnt);\r
1039         if(r != EOK)\r
1040             break;\r
1041 \r
1042         size    -= block_size * fblock_cnt;\r
1043         u8_buf  += block_size * fblock_cnt;\r
1044         f->fpos += block_size * fblock_cnt;\r
1045 \r
1046         if(wcnt)\r
1047             *wcnt += block_size * fblock_cnt;\r
1048 \r
1049         fblock_start = fblock;\r
1050         fblock_cnt = 1;\r
1051     }\r
1052 \r
1053     /*Stop delay cache flush mode*/\r
1054     ext4_block_delay_cache_flush(f->mp->fs.bdev, 0);\r
1055 \r
1056     if(r != EOK)\r
1057         goto Finish;\r
1058 \r
1059     if(size){\r
1060         if(sblock < file_blocks){\r
1061             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1062             if(r != EOK)\r
1063                 goto Finish;\r
1064         }\r
1065         else {\r
1066             r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1067             if(r != EOK)\r
1068                 goto Finish;\r
1069         }\r
1070 \r
1071         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1072         if(r != EOK)\r
1073             goto Finish;\r
1074 \r
1075         memcpy(b.data, u8_buf , size);\r
1076         b.dirty = true;\r
1077 \r
1078         r = ext4_block_set(f->mp->fs.bdev, &b);\r
1079         if(r != EOK)\r
1080             goto Finish;\r
1081 \r
1082         f->fpos += size;\r
1083 \r
1084         if(wcnt)\r
1085             *wcnt += size;\r
1086     }\r
1087 \r
1088     if(f->fpos > f->fsize){\r
1089         f->fsize = f->fpos;\r
1090         ext4_inode_set_size(ref.inode, f->fsize);\r
1091         ref.dirty = true;\r
1092     }\r
1093 \r
1094     Finish:\r
1095     ext4_fs_put_inode_ref(&ref);\r
1096     EXT4_MP_UNLOCK(f->mp);\r
1097     return r;\r
1098 \r
1099 }\r
1100 \r
1101 \r
1102 \r
1103 int ext4_fseek (ext4_file *f, uint64_t offset, uint32_t origin)\r
1104 {\r
1105     switch(origin){\r
1106     case SEEK_SET:\r
1107 \r
1108         if(offset > f->fsize)\r
1109             return EINVAL;\r
1110 \r
1111         f->fpos = offset;\r
1112         return EOK;\r
1113     case SEEK_CUR:\r
1114         if((offset + f->fpos) > f->fsize)\r
1115             return EINVAL;\r
1116 \r
1117         f->fpos += offset;\r
1118         return EOK;\r
1119     case SEEK_END:\r
1120         if(offset > f->fsize)\r
1121             return EINVAL;\r
1122         f->fpos = f->fsize - offset;\r
1123         return EOK;\r
1124 \r
1125     }\r
1126 \r
1127     return EINVAL;\r
1128 }\r
1129 \r
1130 uint64_t ext4_ftell (ext4_file *f)\r
1131 {\r
1132     return  f->fpos;\r
1133 }\r
1134 \r
1135 uint64_t ext4_fsize (ext4_file *f)\r
1136 {\r
1137     return  f->fsize;\r
1138 }\r
1139 \r
1140 /*********************************DIRECTORY OPERATION************************/\r
1141 \r
1142 int ext4_dir_rm(const char *path)\r
1143 {\r
1144     int         r;\r
1145     int         len;\r
1146     ext4_file   f;\r
1147     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1148     struct ext4_inode_ref   current;\r
1149     struct ext4_inode_ref   child;\r
1150 \r
1151     struct ext4_directory_iterator it;\r
1152     uint32_t name_off;\r
1153     uint32_t inode_up;\r
1154     uint32_t inode_current;\r
1155     uint32_t depth = 1;\r
1156     bool     has_children;\r
1157     bool     is_goal;\r
1158     bool     dir_end;\r
1159 \r
1160     if(!mp)\r
1161         return ENOENT;\r
1162 \r
1163     EXT4_MP_LOCK(mp);\r
1164 \r
1165     /*Check if exist.*/\r
1166     r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);\r
1167     if(r != EOK){\r
1168         EXT4_MP_UNLOCK(mp);\r
1169         return r;\r
1170     }\r
1171 \r
1172     path += name_off;\r
1173     len = ext4_path_check(path, &is_goal);\r
1174 \r
1175     inode_current = f.inode;\r
1176     dir_end = false;\r
1177     do {\r
1178         /*Load directory node.*/\r
1179         r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);\r
1180         if(r != EOK){\r
1181             break;\r
1182         }\r
1183 \r
1184         /*Initialize iterator.*/\r
1185         r = ext4_dir_iterator_init(&it, &current, 0);\r
1186         if(r != EOK){\r
1187             ext4_fs_put_inode_ref(&current);\r
1188             break;\r
1189         }\r
1190 \r
1191         while(r == EOK){\r
1192 \r
1193             if(!it.current){\r
1194                 dir_end = true;\r
1195                 break;\r
1196             }\r
1197 \r
1198             /*Get up directory inode when ".." entry*/\r
1199             if((it.current->name_length == 2) &&\r
1200                     ext4_is_dots(it.current->name, it.current->name_length)){\r
1201                 inode_up = it.current->inode;\r
1202             }\r
1203 \r
1204             /*If directory or file entry,  but not "." ".." entry*/\r
1205             if(!ext4_is_dots(it.current->name, it.current->name_length)){\r
1206 \r
1207                 /*Get child inode reference do unlink directory/file.*/\r
1208                 r = ext4_fs_get_inode_ref(&f.mp->fs, it.current->inode, &child);\r
1209                 if(r != EOK)\r
1210                     break;\r
1211 \r
1212                 /*If directory with no leaf children*/\r
1213                 r = ext4_has_children(&has_children, &child);\r
1214                 if(r != EOK){\r
1215                     ext4_fs_put_inode_ref(&child);\r
1216                     break;\r
1217                 }\r
1218 \r
1219                 if(has_children){\r
1220                     /*Has directory children. Go into this tirectory.*/\r
1221                     inode_up = inode_current;\r
1222                     inode_current = it.current->inode;\r
1223                     depth++;\r
1224                     ext4_fs_put_inode_ref(&child);\r
1225                     break;\r
1226                 }\r
1227 \r
1228                 /*Directory is empty. Truncate it.*/\r
1229                 r = ext4_fs_truncate_inode(&child, 0);\r
1230                 if(r != EOK){\r
1231                     ext4_fs_put_inode_ref(&child);\r
1232                     break;\r
1233                 }\r
1234 \r
1235                 /*No children in child directory or file. Just unlink.*/\r
1236                 r = ext4_unlink(f.mp, &current, &child,\r
1237                         (char *)it.current->name, it.current->name_length);\r
1238                 if(r != EOK){\r
1239                     ext4_fs_put_inode_ref(&child);\r
1240                     break;\r
1241                 }\r
1242 \r
1243                 r = ext4_fs_free_inode(&child);\r
1244                 if(r != EOK){\r
1245                     ext4_fs_put_inode_ref(&child);\r
1246                     break;\r
1247                 }\r
1248 \r
1249                 r = ext4_fs_put_inode_ref(&child);\r
1250                 if(r != EOK)\r
1251                     break;\r
1252             }\r
1253 \r
1254             r = ext4_dir_iterator_next(&it);\r
1255         }\r
1256 \r
1257         if(dir_end){\r
1258             /*Directory iterator reached last entry*/\r
1259             ext4_has_children(&has_children, &current);\r
1260             if(!has_children){\r
1261                 inode_current = inode_up;\r
1262                 if(depth)\r
1263                     depth--;\r
1264             }\r
1265             /*Last unlink*/\r
1266             if(!depth){\r
1267                 /*Load parent.*/\r
1268                 struct ext4_inode_ref parent;\r
1269                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up, &parent);\r
1270                 if(r != EOK)\r
1271                     goto End;\r
1272 \r
1273                 r = ext4_fs_truncate_inode(&current, 0);\r
1274                 if(r != EOK){\r
1275                     ext4_fs_put_inode_ref(&parent);\r
1276                     goto End;\r
1277                 }\r
1278 \r
1279                 /* In this place all directories should be unlinked.\r
1280                  * Last unlink from root of current directory*/\r
1281                 r = ext4_unlink(f.mp, &parent, &current, (char *)path, len);\r
1282                 if(r != EOK){\r
1283                     ext4_fs_put_inode_ref(&parent);\r
1284                     goto End;\r
1285                 }\r
1286 \r
1287                 r = ext4_fs_free_inode(&current);\r
1288                 if(r != EOK){\r
1289                     ext4_fs_put_inode_ref(&parent);\r
1290                     goto End;\r
1291                 }\r
1292 \r
1293                 r = ext4_fs_put_inode_ref(&parent);\r
1294                 if(r != EOK)\r
1295                     goto End;\r
1296             }\r
1297         }\r
1298 \r
1299         End:\r
1300         ext4_dir_iterator_fini(&it);\r
1301         ext4_fs_put_inode_ref(&current);\r
1302         dir_end = false;\r
1303 \r
1304         /*When something goes wrong. End loop.*/\r
1305         if(r != EOK)\r
1306             break;\r
1307 \r
1308     }while(depth);\r
1309 \r
1310 \r
1311     EXT4_MP_UNLOCK(mp);\r
1312     return r;\r
1313 }\r
1314 \r
1315 int ext4_dir_mk(const char *path)\r
1316 {\r
1317     int r;\r
1318     ext4_file   f;\r
1319 \r
1320     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1321 \r
1322     if(!mp)\r
1323         return ENOENT;\r
1324 \r
1325     EXT4_MP_LOCK(mp);\r
1326 \r
1327     /*Check if exist.*/\r
1328     r = ext4_generic_open(&f, path, "r", false, 0, 0);\r
1329     if(r == EOK){\r
1330         /*Directory already created*/\r
1331         EXT4_MP_UNLOCK(mp);\r
1332         return r;\r
1333     }\r
1334 \r
1335     /*Create new dir*/\r
1336     r = ext4_generic_open(&f, path, "w", false, 0, 0);\r
1337     if(r != EOK){\r
1338         EXT4_MP_UNLOCK(mp);\r
1339         return r;\r
1340     }\r
1341 \r
1342     EXT4_MP_UNLOCK(mp);\r
1343     return r;\r
1344 }\r
1345 \r
1346 int     ext4_dir_open (ext4_dir *d, const char *path)\r
1347 {\r
1348     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1349     int r;\r
1350 \r
1351     if(!mp)\r
1352         return ENOENT;\r
1353 \r
1354     EXT4_MP_LOCK(mp);\r
1355     r = ext4_generic_open(&d->f, path, "r", false, 0, 0);\r
1356     EXT4_MP_UNLOCK(mp);\r
1357     return r;\r
1358 }\r
1359 \r
1360 int     ext4_dir_close(ext4_dir *d)\r
1361 {\r
1362     return ext4_fclose(&d->f);\r
1363 }\r
1364 \r
1365 ext4_direntry*  ext4_dir_entry_get(ext4_dir *d, uint32_t id)\r
1366 {\r
1367     int          r;\r
1368     uint32_t i;\r
1369     ext4_direntry *de = 0;\r
1370     struct ext4_inode_ref dir;\r
1371     struct ext4_directory_iterator it;\r
1372 \r
1373     EXT4_MP_LOCK(d->f.mp);\r
1374 \r
1375     r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);\r
1376     if(r != EOK){\r
1377         goto Finish;\r
1378     }\r
1379 \r
1380     r = ext4_dir_iterator_init(&it, &dir, 0);\r
1381     if(r != EOK){\r
1382         ext4_fs_put_inode_ref(&dir);\r
1383         goto Finish;\r
1384     }\r
1385 \r
1386     i = 0;\r
1387     while(r == EOK){\r
1388 \r
1389         if(!it.current)\r
1390             break;\r
1391 \r
1392         if(i == id){\r
1393             memcpy(&d->de, it.current, sizeof(ext4_direntry));\r
1394             de = &d->de;\r
1395             break;\r
1396         }\r
1397 \r
1398         i++;\r
1399         r = ext4_dir_iterator_next(&it);\r
1400     }\r
1401 \r
1402     ext4_dir_iterator_fini(&it);\r
1403     ext4_fs_put_inode_ref(&dir);\r
1404 \r
1405     Finish:\r
1406     EXT4_MP_UNLOCK(d->f.mp);\r
1407     return de;\r
1408 }\r
1409 \r
1410 /**\r
1411  * @}\r
1412  */\r