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