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