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