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