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