Add const keyword to mountpoint parameter
[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 = EOK;\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 \r
326     if (!mp)\r
327         return ENOMEM;\r
328 \r
329     r = ext4_block_init(bd);\r
330     if (r != EOK)\r
331         return r;\r
332 \r
333     r = ext4_fs_init(&mp->fs, bd);\r
334     if (r != EOK) {\r
335         ext4_block_fini(bd);\r
336         return r;\r
337     }\r
338 \r
339     bsize = ext4_sb_get_block_size(&mp->fs.sb);\r
340     ext4_block_set_lb_size(bd, bsize);\r
341 \r
342     mp->cache_dynamic = 0;\r
343 \r
344     if (!bc) {\r
345         /*Automatic block cache alloc.*/\r
346         mp->cache_dynamic = 1;\r
347         bc = malloc(sizeof(struct ext4_bcache));\r
348 \r
349         r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);\r
350         if (r != EOK) {\r
351             free(bc);\r
352             ext4_block_fini(bd);\r
353             return r;\r
354         }\r
355     }\r
356 \r
357     if (bsize != bc->itemsize)\r
358         return ENOTSUP;\r
359 \r
360     /*Bind block cache to block device*/\r
361     r = ext4_block_bind_bcache(bd, bc);\r
362     if (r != EOK) {\r
363         ext4_block_fini(bd);\r
364         if (mp->cache_dynamic) {\r
365             ext4_bcache_fini_dynamic(bc);\r
366             free(bc);\r
367         }\r
368         return r;\r
369     }\r
370 \r
371     return r;\r
372 }\r
373 \r
374 int ext4_umount(const char *mount_point)\r
375 {\r
376     int i;\r
377     int r = EOK;\r
378     struct ext4_mountpoint *mp = 0;\r
379 \r
380     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
381         if (!strcmp(_mp[i].name, mount_point)) {\r
382             mp = &_mp[i];\r
383             break;\r
384         }\r
385     }\r
386 \r
387     if (!mp)\r
388         return ENODEV;\r
389 \r
390     r = ext4_fs_fini(&mp->fs);\r
391     if (r != EOK)\r
392         return r;\r
393 \r
394     mp->mounted = 0;\r
395 \r
396     if (mp->cache_dynamic) {\r
397         ext4_bcache_fini_dynamic(mp->fs.bdev->bc);\r
398         free(mp->fs.bdev->bc);\r
399     }\r
400 \r
401     return ext4_block_fini(mp->fs.bdev);\r
402 }\r
403 \r
404 int ext4_mount_point_stats(const char *mount_point,\r
405                            struct ext4_mount_stats *stats)\r
406 {\r
407     uint32_t i;\r
408     struct ext4_mountpoint *mp = 0;\r
409 \r
410     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
411         if (!strcmp(_mp[i].name, mount_point)) {\r
412             mp = &_mp[i];\r
413             break;\r
414         }\r
415     }\r
416     if (!mp)\r
417         return ENOENT;\r
418 \r
419     EXT4_MP_LOCK(mp);\r
420     stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);\r
421     stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);\r
422     stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);\r
423     stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);\r
424     stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);\r
425 \r
426     stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);\r
427     stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);\r
428     stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);\r
429 \r
430     memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);\r
431     EXT4_MP_UNLOCK(mp);\r
432 \r
433     return EOK;\r
434 }\r
435 \r
436 int ext4_mount_setup_locks(const char *mount_point,\r
437                            const struct ext4_lock *locks)\r
438 {\r
439     uint32_t i;\r
440     struct ext4_mountpoint *mp = 0;\r
441 \r
442     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
443         if (!strcmp(_mp[i].name, mount_point)) {\r
444             mp = &_mp[i];\r
445             break;\r
446         }\r
447     }\r
448     if (!mp)\r
449         return ENOENT;\r
450 \r
451     mp->os_locks = locks;\r
452     return EOK;\r
453 }\r
454 \r
455 /********************************FILE OPERATIONS*****************************/\r
456 \r
457 static struct ext4_mountpoint *ext4_get_mount(const char *path)\r
458 {\r
459     int i;\r
460     for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
461 \r
462         if (!_mp[i].mounted)\r
463             continue;\r
464 \r
465         if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))\r
466             return &_mp[i];\r
467     }\r
468     return 0;\r
469 }\r
470 \r
471 static int ext4_path_check(const char *path, bool *is_goal)\r
472 {\r
473     int i;\r
474 \r
475     for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {\r
476 \r
477         if (path[i] == '/') {\r
478             *is_goal = false;\r
479             return i;\r
480         }\r
481 \r
482         if (path[i] == 0) {\r
483             *is_goal = true;\r
484             return i;\r
485         }\r
486     }\r
487 \r
488     return 0;\r
489 }\r
490 \r
491 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)\r
492 {\r
493     if (!flags)\r
494         return false;\r
495 \r
496     if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {\r
497         *file_flags = O_RDONLY;\r
498         return true;\r
499     }\r
500 \r
501     if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {\r
502         *file_flags = O_WRONLY | O_CREAT | O_TRUNC;\r
503         return true;\r
504     }\r
505 \r
506     if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {\r
507         *file_flags = O_WRONLY | O_CREAT | O_APPEND;\r
508         return true;\r
509     }\r
510 \r
511     if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||\r
512         !strcmp(flags, "r+b")) {\r
513         *file_flags = O_RDWR;\r
514         return true;\r
515     }\r
516 \r
517     if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||\r
518         !strcmp(flags, "w+b")) {\r
519         *file_flags = O_RDWR | O_CREAT | O_TRUNC;\r
520         return true;\r
521     }\r
522 \r
523     if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||\r
524         !strcmp(flags, "a+b")) {\r
525         *file_flags = O_RDWR | O_CREAT | O_APPEND;\r
526         return true;\r
527     }\r
528 \r
529     return false;\r
530 }\r
531 \r
532 /****************************************************************************/\r
533 \r
534 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,\r
535                              bool file_expect, uint32_t *parent_inode,\r
536                              uint32_t *name_off)\r
537 {\r
538     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
539     struct ext4_directory_search_result result;\r
540     struct ext4_inode_ref ref;\r
541     bool is_goal = false;\r
542     uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;\r
543     int r = ENOENT;\r
544     uint32_t next_inode;\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     int r;\r
713     int len;\r
714     bool is_goal;\r
715     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
716 \r
717     struct ext4_inode_ref child;\r
718     struct ext4_inode_ref parent;\r
719 \r
720     if (!mp)\r
721         return ENOENT;\r
722 \r
723     EXT4_MP_LOCK(mp);\r
724     r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);\r
725     if (r != EOK) {\r
726         EXT4_MP_UNLOCK(mp);\r
727         return r;\r
728     }\r
729 \r
730     /*Load parent*/\r
731     r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);\r
732     if (r != EOK) {\r
733         EXT4_MP_UNLOCK(mp);\r
734         return r;\r
735     }\r
736 \r
737     /*We have file to delete. Load it.*/\r
738     r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);\r
739     if (r != EOK) {\r
740         ext4_fs_put_inode_ref(&parent);\r
741         EXT4_MP_UNLOCK(mp);\r
742         return r;\r
743     }\r
744 \r
745     /*Turncate.*/\r
746     ext4_block_cache_write_back(mp->fs.bdev, 1);\r
747     /*Truncate may be IO heavy. Do it writeback cache mode.*/\r
748     r = ext4_fs_truncate_inode(&child, 0);\r
749     ext4_block_cache_write_back(mp->fs.bdev, 0);\r
750 \r
751     if (r != EOK)\r
752         goto Finish;\r
753 \r
754     /*Set path*/\r
755     path += name_off;\r
756 \r
757     len = ext4_path_check(path, &is_goal);\r
758 \r
759     /*Unlink from parent.*/\r
760     r = ext4_unlink(mp, &parent, &child, path, len);\r
761     if (r != EOK)\r
762         goto Finish;\r
763 \r
764     r = ext4_fs_free_inode(&child);\r
765     if (r != EOK)\r
766         goto Finish;\r
767 \r
768 Finish:\r
769     ext4_fs_put_inode_ref(&child);\r
770     ext4_fs_put_inode_ref(&parent);\r
771     EXT4_MP_UNLOCK(mp);\r
772     return r;\r
773 }\r
774 \r
775 int ext4_fopen(ext4_file *f, const char *path, const char *flags)\r
776 {\r
777     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
778     int r;\r
779 \r
780     if (!mp)\r
781         return ENOENT;\r
782 \r
783     EXT4_MP_LOCK(mp);\r
784     ext4_block_cache_write_back(mp->fs.bdev, 1);\r
785     r = ext4_generic_open(f, path, flags, true, 0, 0);\r
786     ext4_block_cache_write_back(mp->fs.bdev, 0);\r
787     EXT4_MP_UNLOCK(mp);\r
788     return r;\r
789 }\r
790 \r
791 int ext4_fclose(ext4_file *f)\r
792 {\r
793     ext4_assert(f && f->mp);\r
794 \r
795     f->mp = 0;\r
796     f->flags = 0;\r
797     f->inode = 0;\r
798     f->fpos = f->fsize = 0;\r
799 \r
800     return EOK;\r
801 }\r
802 int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)\r
803 {\r
804     int r = EOK;\r
805     uint32_t u;\r
806     uint32_t fblock;\r
807     uint32_t fblock_start;\r
808     uint32_t fblock_cnt;\r
809     struct ext4_block b;\r
810     uint8_t *u8_buf = buf;\r
811     struct ext4_inode_ref ref;\r
812     uint32_t sblock;\r
813     uint32_t sblock_end;\r
814     uint32_t block_size;\r
815 \r
816     ext4_assert(f && f->mp);\r
817 \r
818     if (f->flags & O_WRONLY)\r
819         return EPERM;\r
820 \r
821     if (!size)\r
822         return EOK;\r
823 \r
824     EXT4_MP_LOCK(f->mp);\r
825 \r
826     if (rcnt)\r
827         *rcnt = 0;\r
828 \r
829     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
830     if (r != EOK) {\r
831         EXT4_MP_UNLOCK(f->mp);\r
832         return r;\r
833     }\r
834 \r
835     /*Sync file size*/\r
836     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
837 \r
838     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
839     size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;\r
840     sblock = (f->fpos) / block_size;\r
841     sblock_end = (f->fpos + size) / block_size;\r
842     u = (f->fpos) % block_size;\r
843 \r
844     if (u) {\r
845 \r
846         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
847 \r
848         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
849         if (r != EOK)\r
850             goto Finish;\r
851 \r
852         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
853         if (r != EOK)\r
854             goto Finish;\r
855 \r
856         memcpy(u8_buf, b.data + u, ll);\r
857 \r
858         r = ext4_block_set(f->mp->fs.bdev, &b);\r
859         if (r != EOK)\r
860             goto Finish;\r
861 \r
862         u8_buf += ll;\r
863         size -= ll;\r
864         f->fpos += ll;\r
865 \r
866         if (rcnt)\r
867             *rcnt += ll;\r
868 \r
869         sblock++;\r
870     }\r
871 \r
872     fblock_start = 0;\r
873     fblock_cnt = 0;\r
874     while (size >= block_size) {\r
875         while (sblock < sblock_end) {\r
876             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
877             if (r != EOK)\r
878                 goto Finish;\r
879 \r
880             sblock++;\r
881 \r
882             if (!fblock_start) {\r
883                 fblock_start = fblock;\r
884             }\r
885 \r
886             if ((fblock_start + fblock_cnt) != fblock)\r
887                 break;\r
888 \r
889             fblock_cnt++;\r
890         }\r
891 \r
892         r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,\r
893                                    fblock_cnt);\r
894         if (r != EOK)\r
895             goto Finish;\r
896 \r
897         size -= block_size * fblock_cnt;\r
898         u8_buf += block_size * fblock_cnt;\r
899         f->fpos += block_size * fblock_cnt;\r
900 \r
901         if (rcnt)\r
902             *rcnt += block_size * fblock_cnt;\r
903 \r
904         fblock_start = fblock;\r
905         fblock_cnt = 1;\r
906     }\r
907 \r
908     if (size) {\r
909         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
910         if (r != EOK)\r
911             goto Finish;\r
912 \r
913         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
914         if (r != EOK)\r
915             goto Finish;\r
916 \r
917         memcpy(u8_buf, b.data, size);\r
918 \r
919         r = ext4_block_set(f->mp->fs.bdev, &b);\r
920         if (r != EOK)\r
921             goto Finish;\r
922 \r
923         f->fpos += size;\r
924 \r
925         if (rcnt)\r
926             *rcnt += size;\r
927     }\r
928 \r
929 Finish:\r
930     ext4_fs_put_inode_ref(&ref);\r
931     EXT4_MP_UNLOCK(f->mp);\r
932     return r;\r
933 }\r
934 \r
935 int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt)\r
936 {\r
937     uint32_t u;\r
938     uint32_t fblock;\r
939 \r
940     uint32_t sblock;\r
941     uint32_t sblock_end;\r
942     uint32_t file_blocks;\r
943     uint32_t block_size;\r
944     uint32_t fblock_start;\r
945     uint32_t fblock_cnt;\r
946 \r
947     struct ext4_block b;\r
948     struct ext4_inode_ref ref;\r
949     const uint8_t *u8_buf = buf;\r
950     int r;\r
951     ;\r
952 \r
953     ext4_assert(f && f->mp);\r
954 \r
955     if (f->flags & O_RDONLY)\r
956         return EPERM;\r
957 \r
958     if (!size)\r
959         return EOK;\r
960 \r
961     EXT4_MP_LOCK(f->mp);\r
962 \r
963     if (wcnt)\r
964         *wcnt = 0;\r
965 \r
966     r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
967     if (r != EOK) {\r
968         EXT4_MP_UNLOCK(f->mp);\r
969         return r;\r
970     }\r
971 \r
972     /*Sync file size*/\r
973     f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
974 \r
975     block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
976 \r
977     sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;\r
978     sblock_end /= block_size;\r
979     file_blocks = (f->fsize / block_size);\r
980 \r
981     if (f->fsize % block_size)\r
982         file_blocks++;\r
983 \r
984     sblock = (f->fpos) / block_size;\r
985 \r
986     u = (f->fpos) % block_size;\r
987 \r
988     if (u) {\r
989         uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
990 \r
991         r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
992         if (r != EOK)\r
993             goto Finish;\r
994 \r
995         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
996         if (r != EOK)\r
997             goto Finish;\r
998 \r
999         memcpy(b.data + u, u8_buf, ll);\r
1000         b.dirty = true;\r
1001 \r
1002         r = ext4_block_set(f->mp->fs.bdev, &b);\r
1003         if (r != EOK)\r
1004             goto Finish;\r
1005 \r
1006         u8_buf += ll;\r
1007         size -= ll;\r
1008         f->fpos += ll;\r
1009 \r
1010         if (wcnt)\r
1011             *wcnt += ll;\r
1012 \r
1013         sblock++;\r
1014     }\r
1015 \r
1016     /*Start write back cache mode.*/\r
1017     r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);\r
1018     if (r != EOK)\r
1019         goto Finish;\r
1020 \r
1021     fblock_start = 0;\r
1022     fblock_cnt = 0;\r
1023     while (size >= block_size) {\r
1024 \r
1025         while (sblock < sblock_end) {\r
1026             if (sblock < file_blocks) {\r
1027                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1028                 if (r != EOK)\r
1029                     break;\r
1030             } else {\r
1031                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1032                 if (r != EOK)\r
1033                     break;\r
1034             }\r
1035 \r
1036             sblock++;\r
1037 \r
1038             if (!fblock_start) {\r
1039                 fblock_start = fblock;\r
1040             }\r
1041 \r
1042             if ((fblock_start + fblock_cnt) != fblock)\r
1043                 break;\r
1044 \r
1045             fblock_cnt++;\r
1046         }\r
1047 \r
1048         r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,\r
1049                                    fblock_cnt);\r
1050         if (r != EOK)\r
1051             break;\r
1052 \r
1053         size -= block_size * fblock_cnt;\r
1054         u8_buf += block_size * fblock_cnt;\r
1055         f->fpos += block_size * fblock_cnt;\r
1056 \r
1057         if (wcnt)\r
1058             *wcnt += block_size * fblock_cnt;\r
1059 \r
1060         fblock_start = fblock;\r
1061         fblock_cnt = 1;\r
1062     }\r
1063 \r
1064     /*Stop write back cache mode*/\r
1065     ext4_block_cache_write_back(f->mp->fs.bdev, 0);\r
1066 \r
1067     if (r != EOK)\r
1068         goto Finish;\r
1069 \r
1070     if (size) {\r
1071         if (sblock < file_blocks) {\r
1072             r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1073             if (r != EOK)\r
1074                 goto Finish;\r
1075         } else {\r
1076             r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1077             if (r != EOK)\r
1078                 goto Finish;\r
1079         }\r
1080 \r
1081         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1082         if (r != EOK)\r
1083             goto Finish;\r
1084 \r
1085         memcpy(b.data, u8_buf, size);\r
1086         b.dirty = true;\r
1087 \r
1088         r = ext4_block_set(f->mp->fs.bdev, &b);\r
1089         if (r != EOK)\r
1090             goto Finish;\r
1091 \r
1092         f->fpos += size;\r
1093 \r
1094         if (wcnt)\r
1095             *wcnt += size;\r
1096     }\r
1097 \r
1098     if (f->fpos > f->fsize) {\r
1099         f->fsize = f->fpos;\r
1100         ext4_inode_set_size(ref.inode, f->fsize);\r
1101         ref.dirty = true;\r
1102     }\r
1103 \r
1104 Finish:\r
1105     ext4_fs_put_inode_ref(&ref);\r
1106     EXT4_MP_UNLOCK(f->mp);\r
1107     return r;\r
1108 }\r
1109 \r
1110 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)\r
1111 {\r
1112     switch (origin) {\r
1113     case SEEK_SET:\r
1114         if (offset > f->fsize)\r
1115             return EINVAL;\r
1116 \r
1117         f->fpos = offset;\r
1118         return EOK;\r
1119     case SEEK_CUR:\r
1120         if ((offset + f->fpos) > f->fsize)\r
1121             return EINVAL;\r
1122 \r
1123         f->fpos += offset;\r
1124         return EOK;\r
1125     case SEEK_END:\r
1126         if (offset > f->fsize)\r
1127             return EINVAL;\r
1128 \r
1129         f->fpos = f->fsize - offset;\r
1130         return EOK;\r
1131     }\r
1132     return EINVAL;\r
1133 }\r
1134 \r
1135 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }\r
1136 \r
1137 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }\r
1138 \r
1139 /*********************************DIRECTORY OPERATION************************/\r
1140 \r
1141 int ext4_dir_rm(const char *path)\r
1142 {\r
1143     int r;\r
1144     int len;\r
1145     ext4_file f;\r
1146 \r
1147     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1148     struct ext4_inode_ref current;\r
1149     struct ext4_inode_ref child;\r
1150     struct ext4_directory_iterator it;\r
1151 \r
1152     uint32_t name_off;\r
1153     uint32_t inode_up;\r
1154     uint32_t inode_current;\r
1155     uint32_t depth = 1;\r
1156 \r
1157     bool has_children;\r
1158     bool is_goal;\r
1159     bool dir_end;\r
1160 \r
1161     if (!mp)\r
1162         return ENOENT;\r
1163 \r
1164     EXT4_MP_LOCK(mp);\r
1165 \r
1166     /*Check if exist.*/\r
1167     r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);\r
1168     if (r != EOK) {\r
1169         EXT4_MP_UNLOCK(mp);\r
1170         return r;\r
1171     }\r
1172 \r
1173     path += name_off;\r
1174     len = ext4_path_check(path, &is_goal);\r
1175 \r
1176     inode_current = f.inode;\r
1177     dir_end = false;\r
1178 \r
1179     ext4_block_cache_write_back(mp->fs.bdev, 1);\r
1180 \r
1181     do {\r
1182         /*Load directory node.*/\r
1183         r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);\r
1184         if (r != EOK) {\r
1185             break;\r
1186         }\r
1187 \r
1188         /*Initialize iterator.*/\r
1189         r = ext4_dir_iterator_init(&it, &current, 0);\r
1190         if (r != EOK) {\r
1191             ext4_fs_put_inode_ref(&current);\r
1192             break;\r
1193         }\r
1194 \r
1195         while (r == EOK) {\r
1196 \r
1197             if (!it.current) {\r
1198                 dir_end = true;\r
1199                 break;\r
1200             }\r
1201 \r
1202             /*Get up directory inode when ".." entry*/\r
1203             if ((it.current->name_length == 2) &&\r
1204                 ext4_is_dots(it.current->name, it.current->name_length)) {\r
1205                 inode_up = it.current->inode;\r
1206             }\r
1207 \r
1208             /*If directory or file entry,  but not "." ".." entry*/\r
1209             if (!ext4_is_dots(it.current->name, it.current->name_length)) {\r
1210 \r
1211                 /*Get child inode reference do unlink directory/file.*/\r
1212                 r = ext4_fs_get_inode_ref(&f.mp->fs, it.current->inode, &child);\r
1213                 if (r != EOK)\r
1214                     break;\r
1215 \r
1216                 /*If directory with no leaf children*/\r
1217                 r = ext4_has_children(&has_children, &child);\r
1218                 if (r != EOK) {\r
1219                     ext4_fs_put_inode_ref(&child);\r
1220                     break;\r
1221                 }\r
1222 \r
1223                 if (has_children) {\r
1224                     /*Has directory children. Go into this tirectory.*/\r
1225                     inode_up = inode_current;\r
1226                     inode_current = it.current->inode;\r
1227                     depth++;\r
1228                     ext4_fs_put_inode_ref(&child);\r
1229                     break;\r
1230                 }\r
1231 \r
1232                 /*Directory is empty. Truncate it.*/\r
1233                 r = ext4_fs_truncate_inode(&child, 0);\r
1234                 if (r != EOK) {\r
1235                     ext4_fs_put_inode_ref(&child);\r
1236                     break;\r
1237                 }\r
1238 \r
1239                 /*No children in child directory or file. Just unlink.*/\r
1240                 r = ext4_unlink(f.mp, &current, &child,\r
1241                                 (char *)it.current->name,\r
1242                                 it.current->name_length);\r
1243                 if (r != EOK) {\r
1244                     ext4_fs_put_inode_ref(&child);\r
1245                     break;\r
1246                 }\r
1247 \r
1248                 r = ext4_fs_free_inode(&child);\r
1249                 if (r != EOK) {\r
1250                     ext4_fs_put_inode_ref(&child);\r
1251                     break;\r
1252                 }\r
1253 \r
1254                 r = ext4_fs_put_inode_ref(&child);\r
1255                 if (r != EOK)\r
1256                     break;\r
1257             }\r
1258 \r
1259             r = ext4_dir_iterator_next(&it);\r
1260         }\r
1261 \r
1262         if (dir_end) {\r
1263             /*Directory iterator reached last entry*/\r
1264             ext4_has_children(&has_children, &current);\r
1265             if (!has_children) {\r
1266                 inode_current = inode_up;\r
1267                 if (depth)\r
1268                     depth--;\r
1269             }\r
1270             /*Last unlink*/\r
1271             if (!depth) {\r
1272                 /*Load parent.*/\r
1273                 struct ext4_inode_ref parent;\r
1274                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up, &parent);\r
1275                 if (r != EOK)\r
1276                     goto End;\r
1277 \r
1278                 r = ext4_fs_truncate_inode(&current, 0);\r
1279                 if (r != EOK) {\r
1280                     ext4_fs_put_inode_ref(&parent);\r
1281                     goto End;\r
1282                 }\r
1283 \r
1284                 /* In this place all directories should be unlinked.\r
1285                  * Last unlink from root of current directory*/\r
1286                 r = ext4_unlink(f.mp, &parent, &current, (char *)path, len);\r
1287                 if (r != EOK) {\r
1288                     ext4_fs_put_inode_ref(&parent);\r
1289                     goto End;\r
1290                 }\r
1291 \r
1292                 r = ext4_fs_free_inode(&current);\r
1293                 if (r != EOK) {\r
1294                     ext4_fs_put_inode_ref(&parent);\r
1295                     goto End;\r
1296                 }\r
1297 \r
1298                 r = ext4_fs_put_inode_ref(&parent);\r
1299                 if (r != EOK)\r
1300                     goto End;\r
1301             }\r
1302         }\r
1303 \r
1304     End:\r
1305         ext4_dir_iterator_fini(&it);\r
1306         ext4_fs_put_inode_ref(&current);\r
1307         dir_end = false;\r
1308 \r
1309         /*When something goes wrong. End loop.*/\r
1310         if (r != EOK)\r
1311             break;\r
1312 \r
1313     } while (depth);\r
1314 \r
1315     ext4_block_cache_write_back(mp->fs.bdev, 0);\r
1316     EXT4_MP_UNLOCK(mp);\r
1317     return r;\r
1318 }\r
1319 \r
1320 int ext4_dir_mk(const char *path)\r
1321 {\r
1322     int r;\r
1323     ext4_file f;\r
1324 \r
1325     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1326 \r
1327     if (!mp)\r
1328         return ENOENT;\r
1329 \r
1330     EXT4_MP_LOCK(mp);\r
1331 \r
1332     /*Check if exist.*/\r
1333     r = ext4_generic_open(&f, path, "r", false, 0, 0);\r
1334     if (r == EOK) {\r
1335         /*Directory already created*/\r
1336         EXT4_MP_UNLOCK(mp);\r
1337         return r;\r
1338     }\r
1339 \r
1340     /*Create new dir*/\r
1341     r = ext4_generic_open(&f, path, "w", false, 0, 0);\r
1342     if (r != EOK) {\r
1343         EXT4_MP_UNLOCK(mp);\r
1344         return r;\r
1345     }\r
1346 \r
1347     EXT4_MP_UNLOCK(mp);\r
1348     return r;\r
1349 }\r
1350 \r
1351 int ext4_dir_open(ext4_dir *d, const char *path)\r
1352 {\r
1353     struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1354     int r;\r
1355 \r
1356     if (!mp)\r
1357         return ENOENT;\r
1358 \r
1359     EXT4_MP_LOCK(mp);\r
1360     r = ext4_generic_open(&d->f, path, "r", false, 0, 0);\r
1361     d->next_off = 0;\r
1362     EXT4_MP_UNLOCK(mp);\r
1363     return r;\r
1364 }\r
1365 \r
1366 int ext4_dir_close(ext4_dir *d) { return ext4_fclose(&d->f); }\r
1367 \r
1368 ext4_direntry *ext4_dir_entry_next(ext4_dir *d)\r
1369 {\r
1370 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)\r
1371 \r
1372     int r;\r
1373     ext4_direntry *de = 0;\r
1374     struct ext4_inode_ref dir;\r
1375     struct ext4_directory_iterator it;\r
1376 \r
1377     EXT4_MP_LOCK(d->f.mp);\r
1378 \r
1379     if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM)\r
1380         return 0;\r
1381 \r
1382     r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);\r
1383     if (r != EOK) {\r
1384         goto Finish;\r
1385     }\r
1386 \r
1387     r = ext4_dir_iterator_init(&it, &dir, d->next_off);\r
1388     if (r != EOK) {\r
1389         ext4_fs_put_inode_ref(&dir);\r
1390         goto Finish;\r
1391     }\r
1392 \r
1393     memcpy(&d->de, it.current, sizeof(ext4_direntry));\r
1394     de = &d->de;\r
1395 \r
1396     ext4_dir_iterator_next(&it);\r
1397 \r
1398     d->next_off = it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;\r
1399 \r
1400     ext4_dir_iterator_fini(&it);\r
1401     ext4_fs_put_inode_ref(&dir);\r
1402 \r
1403 Finish:\r
1404     EXT4_MP_UNLOCK(d->f.mp);\r
1405     return de;\r
1406 }\r
1407 \r
1408 /**\r
1409  * @}\r
1410  */\r