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