1/*
2 * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup lwext4
30 * @{
31 */
32/**
33 * @file ext4.h
34 * @brief Ext4 high level operations (file, directory, mountpoints...)
35 */
36
37#include <ext4_config.h>
38#include <ext4_types.h>
39#include <ext4_misc.h>
40#include <ext4_errno.h>
41#include <ext4_oflags.h>
42#include <ext4_debug.h>
43
44#include <ext4.h>
45#include <ext4_trans.h>
46#include <ext4_blockdev.h>
47#include <ext4_fs.h>
48#include <ext4_dir.h>
49#include <ext4_inode.h>
50#include <ext4_super.h>
51#include <ext4_block_group.h>
52#include <ext4_dir_idx.h>
53#include <ext4_xattr.h>
54#include <ext4_journal.h>
55
56
57#include <stdlib.h>
58#include <string.h>
59
60/**@brief Mount point OS dependent lock*/
61#define EXT4_MP_LOCK(_m) \
62 do { \
63 if ((_m)->os_locks) \
64 (_m)->os_locks->lock(); \
65 } while (0)
66
67/**@brief Mount point OS dependent unlock*/
68#define EXT4_MP_UNLOCK(_m) \
69 do { \
70 if ((_m)->os_locks) \
71 (_m)->os_locks->unlock(); \
72 } while (0)
73
74/**@brief Block devices descriptor.*/
75struct ext4_block_devices {
76
77 /**@brief Block device name.*/
78 char name[CONFIG_EXT4_MAX_BLOCKDEV_NAME + 1];
79
80 /**@brief Block device handle.*/
81 struct ext4_blockdev *bd;
82};
83
84/**@brief Block devices.*/
85static struct ext4_block_devices s_bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
86
87/**@brief Mountpoints.*/
88static struct ext4_mountpoint s_mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
89
90int ext4_device_register(struct ext4_blockdev *bd,
91 const char *dev_name)
92{
93 ext4_assert(bd && dev_name);
94
95 if (strlen(s: dev_name) > CONFIG_EXT4_MAX_BLOCKDEV_NAME)
96 return EINVAL;
97
98 for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
99 if (!strcmp(a: s_bdevices[i].name, b: dev_name))
100 return EEXIST;
101 }
102
103 for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
104 if (!s_bdevices[i].bd) {
105 strcpy(dest: s_bdevices[i].name, src: dev_name);
106 s_bdevices[i].bd = bd;
107 return EOK;
108 }
109 }
110
111 return ENOSPC;
112}
113
114int ext4_device_unregister(const char *dev_name)
115{
116 ext4_assert(dev_name);
117
118 for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
119 if (strcmp(a: s_bdevices[i].name, b: dev_name))
120 continue;
121
122 memset(dest: &s_bdevices[i], c: 0, size: sizeof(s_bdevices[i]));
123 return EOK;
124 }
125
126 return ENOENT;
127}
128
129int ext4_device_unregister_all(void)
130{
131 memset(dest: s_bdevices, c: 0, size: sizeof(s_bdevices));
132
133 return EOK;
134}
135
136/****************************************************************************/
137
138static bool ext4_is_dots(const uint8_t *name, size_t name_size)
139{
140 if ((name_size == 1) && (name[0] == '.'))
141 return true;
142
143 if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
144 return true;
145
146 return false;
147}
148
149static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
150{
151 struct ext4_sblock *sb = &enode->fs->sb;
152
153 /* Check if node is directory */
154 if (!ext4_inode_is_type(sb, inode: enode->inode, EXT4_INODE_MODE_DIRECTORY)) {
155 *has_children = false;
156 return EOK;
157 }
158
159 struct ext4_dir_iter it;
160 int rc = ext4_dir_iterator_init(it: &it, inode_ref: enode, pos: 0);
161 if (rc != EOK)
162 return rc;
163
164 /* Find a non-empty directory entry */
165 bool found = false;
166 while (it.curr != NULL) {
167 if (ext4_dir_en_get_inode(de: it.curr) != 0) {
168 uint16_t nsize;
169 nsize = ext4_dir_en_get_name_len(sb, de: it.curr);
170 if (!ext4_is_dots(name: it.curr->name, name_size: nsize)) {
171 found = true;
172 break;
173 }
174 }
175
176 rc = ext4_dir_iterator_next(it: &it);
177 if (rc != EOK) {
178 ext4_dir_iterator_fini(it: &it);
179 return rc;
180 }
181 }
182
183 rc = ext4_dir_iterator_fini(it: &it);
184 if (rc != EOK)
185 return rc;
186
187 *has_children = found;
188
189 return EOK;
190}
191
192int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,
193 struct ext4_inode_ref *ch, const char *n,
194 uint32_t len, bool rename)
195{
196 /* Check maximum name length */
197 if (len > EXT4_DIRECTORY_FILENAME_LEN)
198 return EINVAL;
199
200 /* Add entry to parent directory */
201 int r = ext4_dir_add_entry(parent, name: n, name_len: len, child: ch);
202 if (r != EOK)
203 return r;
204
205 /* Fill new dir -> add '.' and '..' entries.
206 * Also newly allocated inode should have 0 link count.
207 */
208
209 bool is_dir = ext4_inode_is_type(sb: &mp->fs.sb, inode: ch->inode,
210 EXT4_INODE_MODE_DIRECTORY);
211 if (is_dir && !rename) {
212
213#if CONFIG_DIR_INDEX_ENABLE
214 /* Initialize directory index if supported */
215 if (ext4_sb_feature_com(s: &mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
216 r = ext4_dir_dx_init(dir: ch, parent);
217 if (r != EOK)
218 return r;
219
220 ext4_inode_set_flag(inode: ch->inode, EXT4_INODE_FLAG_INDEX);
221 ch->dirty = true;
222 } else
223#endif
224 {
225 r = ext4_dir_add_entry(parent: ch, name: ".", name_len: strlen(s: "."), child: ch);
226 if (r != EOK) {
227 ext4_dir_remove_entry(parent, name: n, name_len: strlen(s: n));
228 return r;
229 }
230
231 r = ext4_dir_add_entry(parent: ch, name: "..", name_len: strlen(s: ".."), child: parent);
232 if (r != EOK) {
233 ext4_dir_remove_entry(parent, name: n, name_len: strlen(s: n));
234 ext4_dir_remove_entry(parent: ch, name: ".", name_len: strlen(s: "."));
235 return r;
236 }
237 }
238
239 /*New empty directory. Two links (. and ..) */
240 ext4_inode_set_links_cnt(inode: ch->inode, cnt: 2);
241 ext4_fs_inode_links_count_inc(inode_ref: parent);
242 ch->dirty = true;
243 parent->dirty = true;
244 return r;
245 }
246 /*
247 * In case we want to rename a directory,
248 * we reset the original '..' pointer.
249 */
250 if (is_dir) {
251 bool idx;
252 idx = ext4_inode_has_flag(inode: ch->inode, EXT4_INODE_FLAG_INDEX);
253 struct ext4_dir_search_result res;
254 if (!idx) {
255 r = ext4_dir_find_entry(result: &res, parent: ch, name: "..", name_len: strlen(s: ".."));
256 if (r != EOK)
257 return EIO;
258
259 ext4_dir_en_set_inode(de: res.dentry, inode: parent->index);
260 ext4_trans_set_block_dirty(buf: res.block.buf);
261 r = ext4_dir_destroy_result(parent: ch, result: &res);
262 if (r != EOK)
263 return r;
264
265 } else {
266#if CONFIG_DIR_INDEX_ENABLE
267 r = ext4_dir_dx_reset_parent_inode(dir: ch, parent_inode: parent->index);
268 if (r != EOK)
269 return r;
270
271#endif
272 }
273
274 ext4_fs_inode_links_count_inc(inode_ref: parent);
275 parent->dirty = true;
276 }
277 if (!rename) {
278 ext4_fs_inode_links_count_inc(inode_ref: ch);
279 ch->dirty = true;
280 }
281
282 return r;
283}
284
285int ext4_unlink(struct ext4_mountpoint *mp,
286 struct ext4_inode_ref *parent,
287 struct ext4_inode_ref *child, const char *name,
288 uint32_t name_len)
289{
290 bool has_children;
291 int rc = ext4_has_children(has_children: &has_children, enode: child);
292 if (rc != EOK)
293 return rc;
294
295 /* Cannot unlink non-empty node */
296 if (has_children)
297 return ENOTEMPTY;
298
299 /* Remove entry from parent directory */
300 rc = ext4_dir_remove_entry(parent, name, name_len);
301 if (rc != EOK)
302 return rc;
303
304 bool is_dir = ext4_inode_is_type(sb: &mp->fs.sb, inode: child->inode,
305 EXT4_INODE_MODE_DIRECTORY);
306
307 /* If directory - handle links from parent */
308 if (is_dir) {
309 ext4_fs_inode_links_count_dec(inode_ref: parent);
310 parent->dirty = true;
311 }
312
313 /*
314 * TODO: Update timestamps of the parent
315 * (when we have wall-clock time).
316 *
317 * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
318 * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
319 * parent->dirty = true;
320 */
321
322 /*
323 * TODO: Update timestamp for inode.
324 *
325 * ext4_inode_set_change_inode_time(child->inode,
326 * (uint32_t) now);
327 */
328 if (ext4_inode_get_links_cnt(inode: child->inode)) {
329 ext4_fs_inode_links_count_dec(inode_ref: child);
330 child->dirty = true;
331 }
332
333 return EOK;
334}
335
336/****************************************************************************/
337
338int ext4_mount(const char *dev_name, const char *mount_point,
339 bool read_only)
340{
341 int r;
342 uint32_t bsize;
343 struct ext4_bcache *bc;
344 struct ext4_blockdev *bd = 0;
345 struct ext4_mountpoint *mp = 0;
346
347 ext4_assert(mount_point && dev_name);
348
349 size_t mp_len = strlen(s: mount_point);
350
351 if (mp_len > CONFIG_EXT4_MAX_MP_NAME)
352 return EINVAL;
353
354 if (mount_point[mp_len - 1] != '/')
355 return ENOTSUP;
356
357 for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
358 if (!strcmp(a: dev_name, b: s_bdevices[i].name)) {
359 bd = s_bdevices[i].bd;
360 break;
361 }
362 }
363
364 if (!bd)
365 return ENODEV;
366
367 for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
368 if (!s_mp[i].mounted) {
369 strcpy(dest: s_mp[i].name, src: mount_point);
370 mp = &s_mp[i];
371 break;
372 }
373
374 if (!strcmp(a: s_mp[i].name, b: mount_point))
375 return EOK;
376 }
377
378 if (!mp)
379 return ENOMEM;
380
381 r = ext4_block_init(bdev: bd);
382 if (r != EOK)
383 return r;
384
385 r = ext4_fs_init(fs: &mp->fs, bdev: bd, read_only);
386 if (r != EOK) {
387 ext4_block_fini(bdev: bd);
388 return r;
389 }
390
391 bsize = ext4_sb_get_block_size(s: &mp->fs.sb);
392 ext4_block_set_lb_size(bdev: bd, lb_bsize: bsize);
393 bc = &mp->bc;
394
395 r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, itemsize: bsize);
396 if (r != EOK) {
397 ext4_block_fini(bdev: bd);
398 return r;
399 }
400
401 if (bsize != bc->itemsize)
402 return ENOTSUP;
403
404 /*Bind block cache to block device*/
405 r = ext4_block_bind_bcache(bdev: bd, bc);
406 if (r != EOK) {
407 ext4_bcache_cleanup(bc);
408 ext4_block_fini(bdev: bd);
409 ext4_bcache_fini_dynamic(bc);
410 return r;
411 }
412
413 bd->fs = &mp->fs;
414 mp->mounted = 1;
415 return r;
416}
417
418
419int ext4_umount(const char *mount_point)
420{
421 int i;
422 int r;
423 struct ext4_mountpoint *mp = 0;
424
425 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
426 if (!strcmp(a: s_mp[i].name, b: mount_point)) {
427 mp = &s_mp[i];
428 break;
429 }
430 }
431
432 if (!mp)
433 return ENODEV;
434
435 r = ext4_fs_fini(fs: &mp->fs);
436 if (r != EOK)
437 goto Finish;
438
439 mp->mounted = 0;
440
441 ext4_bcache_cleanup(bc: mp->fs.bdev->bc);
442 ext4_bcache_fini_dynamic(bc: mp->fs.bdev->bc);
443
444 r = ext4_block_fini(bdev: mp->fs.bdev);
445Finish:
446 mp->fs.bdev->fs = NULL;
447 return r;
448}
449
450struct ext4_mountpoint *ext4_get_mount(const char *path)
451{
452 for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
453
454 if (!s_mp[i].mounted)
455 continue;
456
457 if (!strncmp(a: s_mp[i].name, b: path, max_size: strlen(s: s_mp[i].name)))
458 return &s_mp[i];
459 }
460
461 return NULL;
462}
463
464__unused
465static int __ext4_journal_start(const char *mount_point)
466{
467 int r = EOK;
468 struct ext4_mountpoint *mp = ext4_get_mount(path: mount_point);
469
470 if (!mp)
471 return ENOENT;
472
473 if (mp->fs.read_only)
474 return EOK;
475
476 if (ext4_sb_feature_com(s: &mp->fs.sb,
477 EXT4_FCOM_HAS_JOURNAL)) {
478 r = jbd_get_fs(fs: &mp->fs, jbd_fs: &mp->jbd_fs);
479 if (r != EOK)
480 goto Finish;
481
482 r = jbd_journal_start(jbd_fs: &mp->jbd_fs, journal: &mp->jbd_journal);
483 if (r != EOK) {
484 mp->jbd_fs.dirty = false;
485 jbd_put_fs(jbd_fs: &mp->jbd_fs);
486 goto Finish;
487 }
488 mp->fs.jbd_fs = &mp->jbd_fs;
489 mp->fs.jbd_journal = &mp->jbd_journal;
490 }
491Finish:
492 return r;
493}
494
495__unused
496static int __ext4_journal_stop(const char *mount_point)
497{
498 int r = EOK;
499 struct ext4_mountpoint *mp = ext4_get_mount(path: mount_point);
500
501 if (!mp)
502 return ENOENT;
503
504 if (mp->fs.read_only)
505 return EOK;
506
507 if (ext4_sb_feature_com(s: &mp->fs.sb,
508 EXT4_FCOM_HAS_JOURNAL)) {
509 r = jbd_journal_stop(journal: &mp->jbd_journal);
510 if (r != EOK) {
511 mp->jbd_fs.dirty = false;
512 jbd_put_fs(jbd_fs: &mp->jbd_fs);
513 mp->fs.jbd_journal = NULL;
514 mp->fs.jbd_fs = NULL;
515 goto Finish;
516 }
517
518 r = jbd_put_fs(jbd_fs: &mp->jbd_fs);
519 if (r != EOK) {
520 mp->fs.jbd_journal = NULL;
521 mp->fs.jbd_fs = NULL;
522 goto Finish;
523 }
524
525 mp->fs.jbd_journal = NULL;
526 mp->fs.jbd_fs = NULL;
527 }
528Finish:
529 return r;
530}
531
532__unused
533static int __ext4_recover(const char *mount_point)
534{
535 struct ext4_mountpoint *mp = ext4_get_mount(path: mount_point);
536 int r = ENOTSUP;
537
538 if (!mp)
539 return ENOENT;
540
541 EXT4_MP_LOCK(mp);
542 if (ext4_sb_feature_com(s: &mp->fs.sb, EXT4_FCOM_HAS_JOURNAL)) {
543 struct jbd_fs *jbd_fs = ext4_calloc(count: 1, size: sizeof(struct jbd_fs));
544 if (!jbd_fs) {
545 r = ENOMEM;
546 goto Finish;
547 }
548
549 r = jbd_get_fs(fs: &mp->fs, jbd_fs);
550 if (r != EOK) {
551 ext4_free(pointer: jbd_fs);
552 goto Finish;
553 }
554
555 r = jbd_recover(jbd_fs);
556 jbd_put_fs(jbd_fs);
557 ext4_free(pointer: jbd_fs);
558 }
559 if (r == EOK && !mp->fs.read_only) {
560 uint32_t bgid;
561 uint64_t free_blocks_count = 0;
562 uint32_t free_inodes_count = 0;
563 struct ext4_block_group_ref bg_ref;
564
565 /* Update superblock's stats */
566 for (bgid = 0;bgid < ext4_block_group_cnt(s: &mp->fs.sb);bgid++) {
567 r = ext4_fs_get_block_group_ref(fs: &mp->fs, bgid, ref: &bg_ref);
568 if (r != EOK)
569 goto Finish;
570
571 free_blocks_count +=
572 ext4_bg_get_free_blocks_count(bg: bg_ref.block_group,
573 s: &mp->fs.sb);
574 free_inodes_count +=
575 ext4_bg_get_free_inodes_count(bg: bg_ref.block_group,
576 s: &mp->fs.sb);
577
578 ext4_fs_put_block_group_ref(ref: &bg_ref);
579 }
580 ext4_sb_set_free_blocks_cnt(s: &mp->fs.sb, cnt: free_blocks_count);
581 ext4_set32(&mp->fs.sb, free_inodes_count, free_inodes_count);
582 /* We don't need to save the superblock stats immediately. */
583 }
584
585Finish:
586 EXT4_MP_UNLOCK(mp);
587 return r;
588}
589
590__unused
591static int __ext4_trans_start(struct ext4_mountpoint *mp)
592{
593 int r = EOK;
594
595 if (mp->fs.jbd_journal && !mp->fs.curr_trans) {
596 struct jbd_journal *journal = mp->fs.jbd_journal;
597 struct jbd_trans *trans;
598 trans = jbd_journal_new_trans(journal);
599 if (!trans) {
600 r = ENOMEM;
601 goto Finish;
602 }
603 mp->fs.curr_trans = trans;
604 }
605Finish:
606 return r;
607}
608
609__unused
610static int __ext4_trans_stop(struct ext4_mountpoint *mp)
611{
612 int r = EOK;
613
614 if (mp->fs.jbd_journal && mp->fs.curr_trans) {
615 struct jbd_journal *journal = mp->fs.jbd_journal;
616 struct jbd_trans *trans = mp->fs.curr_trans;
617 r = jbd_journal_commit_trans(journal, trans);
618 mp->fs.curr_trans = NULL;
619 }
620 return r;
621}
622
623__unused
624static void __ext4_trans_abort(struct ext4_mountpoint *mp)
625{
626 if (mp->fs.jbd_journal && mp->fs.curr_trans) {
627 struct jbd_journal *journal = mp->fs.jbd_journal;
628 struct jbd_trans *trans = mp->fs.curr_trans;
629 jbd_journal_free_trans(journal, trans, abort: true);
630 mp->fs.curr_trans = NULL;
631 }
632}
633
634int ext4_journal_start(const char *mount_point __unused)
635{
636 int r = EOK;
637#if CONFIG_JOURNALING_ENABLE
638 r = __ext4_journal_start(mount_point);
639#endif
640 return r;
641}
642
643int ext4_journal_stop(const char *mount_point __unused)
644{
645 int r = EOK;
646#if CONFIG_JOURNALING_ENABLE
647 r = __ext4_journal_stop(mount_point);
648#endif
649 return r;
650}
651
652int ext4_recover(const char *mount_point __unused)
653{
654 int r = EOK;
655#if CONFIG_JOURNALING_ENABLE
656 r = __ext4_recover(mount_point);
657#endif
658 return r;
659}
660
661static int ext4_trans_start(struct ext4_mountpoint *mp __unused)
662{
663 int r = EOK;
664#if CONFIG_JOURNALING_ENABLE
665 r = __ext4_trans_start(mp);
666#endif
667 return r;
668}
669
670static int ext4_trans_stop(struct ext4_mountpoint *mp __unused)
671{
672 int r = EOK;
673#if CONFIG_JOURNALING_ENABLE
674 r = __ext4_trans_stop(mp);
675#endif
676 return r;
677}
678
679static void ext4_trans_abort(struct ext4_mountpoint *mp __unused)
680{
681#if CONFIG_JOURNALING_ENABLE
682 __ext4_trans_abort(mp);
683#endif
684}
685
686
687int ext4_mount_point_stats(const char *mount_point,
688 struct ext4_mount_stats *stats)
689{
690 struct ext4_mountpoint *mp = ext4_get_mount(path: mount_point);
691
692 if (!mp)
693 return ENOENT;
694
695 EXT4_MP_LOCK(mp);
696 stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
697 stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
698 stats->blocks_count = ext4_sb_get_blocks_cnt(s: &mp->fs.sb);
699 stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(s: &mp->fs.sb);
700 stats->block_size = ext4_sb_get_block_size(s: &mp->fs.sb);
701
702 stats->block_group_count = ext4_block_group_cnt(s: &mp->fs.sb);
703 stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);
704 stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);
705
706 memcpy(dest: stats->volume_name, src: mp->fs.sb.volume_name, size: 16);
707 EXT4_MP_UNLOCK(mp);
708
709 return EOK;
710}
711
712int ext4_mount_setup_locks(const char *mount_point,
713 const struct ext4_lock *locks)
714{
715 uint32_t i;
716 struct ext4_mountpoint *mp = 0;
717
718 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
719 if (!strcmp(a: s_mp[i].name, b: mount_point)) {
720 mp = &s_mp[i];
721 break;
722 }
723 }
724 if (!mp)
725 return ENOENT;
726
727 mp->os_locks = locks;
728 return EOK;
729}
730
731/********************************FILE OPERATIONS*****************************/
732
733static int ext4_path_check(const char *path, bool *is_goal)
734{
735 int i;
736
737 for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
738
739 if (path[i] == '/') {
740 *is_goal = false;
741 return i;
742 }
743
744 if (path[i] == 0) {
745 *is_goal = true;
746 return i;
747 }
748 }
749
750 return 0;
751}
752
753static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
754{
755 if (!flags)
756 return false;
757
758 if (!strcmp(a: flags, b: "r") || !strcmp(a: flags, b: "rb")) {
759 *file_flags = O_RDONLY;
760 return true;
761 }
762
763 if (!strcmp(a: flags, b: "w") || !strcmp(a: flags, b: "wb")) {
764 *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
765 return true;
766 }
767
768 if (!strcmp(a: flags, b: "a") || !strcmp(a: flags, b: "ab")) {
769 *file_flags = O_WRONLY | O_CREAT | O_APPEND;
770 return true;
771 }
772
773 if (!strcmp(a: flags, b: "r+") || !strcmp(a: flags, b: "rb+") ||
774 !strcmp(a: flags, b: "r+b")) {
775 *file_flags = O_RDWR;
776 return true;
777 }
778
779 if (!strcmp(a: flags, b: "w+") || !strcmp(a: flags, b: "wb+") ||
780 !strcmp(a: flags, b: "w+b")) {
781 *file_flags = O_RDWR | O_CREAT | O_TRUNC;
782 return true;
783 }
784
785 if (!strcmp(a: flags, b: "a+") || !strcmp(a: flags, b: "ab+") ||
786 !strcmp(a: flags, b: "a+b")) {
787 *file_flags = O_RDWR | O_CREAT | O_APPEND;
788 return true;
789 }
790
791 return false;
792}
793
794static int ext4_trunc_inode(struct ext4_mountpoint *mp,
795 uint32_t index, uint64_t new_size)
796{
797 int r = EOK;
798 struct ext4_fs *const fs = &mp->fs;
799 struct ext4_inode_ref inode_ref;
800 uint64_t inode_size;
801 bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
802 r = ext4_fs_get_inode_ref(fs, index, ref: &inode_ref);
803 if (r != EOK)
804 return r;
805
806 inode_size = ext4_inode_get_size(sb: &fs->sb, inode: inode_ref.inode);
807 ext4_fs_put_inode_ref(ref: &inode_ref);
808 if (has_trans)
809 ext4_trans_stop(mp);
810
811 while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
812
813 inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
814
815 ext4_trans_start(mp);
816 r = ext4_fs_get_inode_ref(fs, index, ref: &inode_ref);
817 if (r != EOK) {
818 ext4_trans_abort(mp);
819 break;
820 }
821 r = ext4_fs_truncate_inode(inode_ref: &inode_ref, new_size: inode_size);
822 if (r != EOK)
823 ext4_fs_put_inode_ref(ref: &inode_ref);
824 else
825 r = ext4_fs_put_inode_ref(ref: &inode_ref);
826
827 if (r != EOK) {
828 ext4_trans_abort(mp);
829 goto Finish;
830 } else
831 ext4_trans_stop(mp);
832 }
833
834 if (inode_size > new_size) {
835
836 inode_size = new_size;
837
838 ext4_trans_start(mp);
839 r = ext4_fs_get_inode_ref(fs, index, ref: &inode_ref);
840 if (r != EOK) {
841 ext4_trans_abort(mp);
842 goto Finish;
843 }
844 r = ext4_fs_truncate_inode(inode_ref: &inode_ref, new_size: inode_size);
845 if (r != EOK)
846 ext4_fs_put_inode_ref(ref: &inode_ref);
847 else
848 r = ext4_fs_put_inode_ref(ref: &inode_ref);
849
850 if (r != EOK)
851 ext4_trans_abort(mp);
852 else
853 ext4_trans_stop(mp);
854
855 }
856
857Finish:
858
859 if (has_trans)
860 ext4_trans_start(mp);
861
862 return r;
863}
864
865static int ext4_trunc_dir(struct ext4_mountpoint *mp,
866 struct ext4_inode_ref *parent,
867 struct ext4_inode_ref *dir)
868{
869 int r = EOK;
870 bool is_dir = ext4_inode_is_type(sb: &mp->fs.sb, inode: dir->inode,
871 EXT4_INODE_MODE_DIRECTORY);
872 uint32_t block_size = ext4_sb_get_block_size(s: &mp->fs.sb);
873 if (!is_dir)
874 return EINVAL;
875
876#if CONFIG_DIR_INDEX_ENABLE
877 /* Initialize directory index if supported */
878 if (ext4_sb_feature_com(s: &mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
879 r = ext4_dir_dx_init(dir, parent);
880 if (r != EOK)
881 return r;
882
883 r = ext4_trunc_inode(mp, index: dir->index,
884 EXT4_DIR_DX_INIT_BCNT * block_size);
885 if (r != EOK)
886 return r;
887 } else
888#endif
889 {
890 r = ext4_trunc_inode(mp, index: dir->index, new_size: block_size);
891 if (r != EOK)
892 return r;
893 }
894
895 return ext4_fs_truncate_inode(inode_ref: dir, new_size: 0);
896}
897
898/*
899 * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
900 * any filetype of the target dir entry will be accepted.
901 */
902static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
903 int ftype, uint32_t *parent_inode,
904 uint32_t *name_off)
905{
906 bool is_goal = false;
907 uint32_t imode = EXT4_INODE_MODE_DIRECTORY;
908 uint32_t next_inode;
909
910 int r;
911 int len;
912 struct ext4_mountpoint *mp = ext4_get_mount(path);
913 struct ext4_dir_search_result result;
914 struct ext4_inode_ref ref;
915
916 f->mp = 0;
917
918 if (!mp)
919 return ENOENT;
920
921 struct ext4_fs *const fs = &mp->fs;
922 struct ext4_sblock *const sb = &mp->fs.sb;
923
924 if (fs->read_only && flags & O_CREAT)
925 return EROFS;
926
927 f->flags = flags;
928
929 /*Skip mount point*/
930 path += strlen(s: mp->name);
931
932 if (name_off)
933 *name_off = strlen(s: mp->name);
934
935 /*Load root*/
936 r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, ref: &ref);
937 if (r != EOK)
938 return r;
939
940 if (parent_inode)
941 *parent_inode = ref.index;
942
943 len = ext4_path_check(path, is_goal: &is_goal);
944 while (1) {
945
946 len = ext4_path_check(path, is_goal: &is_goal);
947 if (!len) {
948 /*If root open was request.*/
949 if (ftype == EXT4_DE_DIR || ftype == EXT4_DE_UNKNOWN)
950 if (is_goal)
951 break;
952
953 r = ENOENT;
954 break;
955 }
956
957 r = ext4_dir_find_entry(result: &result, parent: &ref, name: path, name_len: len);
958 if (r != EOK) {
959
960 /*Destroy last result*/
961 ext4_dir_destroy_result(parent: &ref, result: &result);
962 if (r != ENOENT)
963 break;
964
965 if (!(f->flags & O_CREAT))
966 break;
967
968 /*O_CREAT allows create new entry*/
969 struct ext4_inode_ref child_ref;
970 r = ext4_fs_alloc_inode(fs, inode_ref: &child_ref,
971 filetype: is_goal ? ftype : EXT4_DE_DIR);
972
973 if (r != EOK)
974 break;
975
976 ext4_fs_inode_blocks_init(fs, inode_ref: &child_ref);
977
978 /*Link with root dir.*/
979 r = ext4_link(mp, parent: &ref, ch: &child_ref, n: path, len, rename: false);
980 if (r != EOK) {
981 /*Fail. Free new inode.*/
982 ext4_fs_free_inode(inode_ref: &child_ref);
983 /*We do not want to write new inode.
984 But block has to be released.*/
985 child_ref.dirty = false;
986 ext4_fs_put_inode_ref(ref: &child_ref);
987 break;
988 }
989
990 ext4_fs_put_inode_ref(ref: &child_ref);
991 continue;
992 }
993
994 if (parent_inode)
995 *parent_inode = ref.index;
996
997 next_inode = ext4_dir_en_get_inode(de: result.dentry);
998 if (ext4_sb_feature_incom(s: sb, EXT4_FINCOM_FILETYPE)) {
999 uint8_t t;
1000 t = ext4_dir_en_get_inode_type(sb, de: result.dentry);
1001 imode = ext4_fs_correspond_inode_mode(filetype: t);
1002 } else {
1003 struct ext4_inode_ref child_ref;
1004 r = ext4_fs_get_inode_ref(fs, index: next_inode, ref: &child_ref);
1005 if (r != EOK)
1006 break;
1007
1008 imode = ext4_inode_type(sb, inode: child_ref.inode);
1009 ext4_fs_put_inode_ref(ref: &child_ref);
1010 }
1011
1012 r = ext4_dir_destroy_result(parent: &ref, result: &result);
1013 if (r != EOK)
1014 break;
1015
1016 /*If expected file error*/
1017 if (imode != EXT4_INODE_MODE_DIRECTORY && !is_goal) {
1018 r = ENOENT;
1019 break;
1020 }
1021 if (ftype != EXT4_DE_UNKNOWN) {
1022 bool df = imode != ext4_fs_correspond_inode_mode(filetype: ftype);
1023 if (df && is_goal) {
1024 r = ENOENT;
1025 break;
1026 }
1027 }
1028
1029 r = ext4_fs_put_inode_ref(ref: &ref);
1030 if (r != EOK)
1031 break;
1032
1033 r = ext4_fs_get_inode_ref(fs, index: next_inode, ref: &ref);
1034 if (r != EOK)
1035 break;
1036
1037 if (is_goal)
1038 break;
1039
1040 path += len + 1;
1041
1042 if (name_off)
1043 *name_off += len + 1;
1044 }
1045
1046 if (r != EOK) {
1047 ext4_fs_put_inode_ref(ref: &ref);
1048 return r;
1049 }
1050
1051 if (is_goal) {
1052
1053 if ((f->flags & O_TRUNC) && (imode == EXT4_INODE_MODE_FILE)) {
1054 r = ext4_trunc_inode(mp, index: ref.index, new_size: 0);
1055 if (r != EOK) {
1056 ext4_fs_put_inode_ref(ref: &ref);
1057 return r;
1058 }
1059 }
1060
1061 f->mp = mp;
1062 f->fsize = ext4_inode_get_size(sb, inode: ref.inode);
1063 f->inode = ref.index;
1064 f->fpos = 0;
1065
1066 if (f->flags & O_APPEND)
1067 f->fpos = f->fsize;
1068 }
1069
1070 return ext4_fs_put_inode_ref(ref: &ref);
1071}
1072
1073/****************************************************************************/
1074
1075static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
1076 bool file_expect, uint32_t *parent_inode,
1077 uint32_t *name_off)
1078{
1079 uint32_t iflags;
1080 int filetype;
1081 int r;
1082 struct ext4_mountpoint *mp = ext4_get_mount(path);
1083
1084 if (ext4_parse_flags(flags, file_flags: &iflags) == false)
1085 return EINVAL;
1086
1087 if (file_expect == true)
1088 filetype = EXT4_DE_REG_FILE;
1089 else
1090 filetype = EXT4_DE_DIR;
1091
1092 if (iflags & O_CREAT)
1093 ext4_trans_start(mp);
1094
1095 r = ext4_generic_open2(f, path, flags: iflags, ftype: filetype, parent_inode,
1096 name_off);
1097
1098 if (iflags & O_CREAT) {
1099 if (r == EOK)
1100 ext4_trans_stop(mp);
1101 else
1102 ext4_trans_abort(mp);
1103 }
1104
1105 return r;
1106}
1107
1108static int ext4_create_hardlink(const char *path,
1109 struct ext4_inode_ref *child_ref, bool rename)
1110{
1111 bool is_goal = false;
1112 uint32_t inode_mode = EXT4_INODE_MODE_DIRECTORY;
1113 uint32_t next_inode;
1114
1115 int r;
1116 int len;
1117 struct ext4_mountpoint *mp = ext4_get_mount(path);
1118 struct ext4_dir_search_result result;
1119 struct ext4_inode_ref ref;
1120
1121 if (!mp)
1122 return ENOENT;
1123
1124 struct ext4_fs *const fs = &mp->fs;
1125 struct ext4_sblock *const sb = &mp->fs.sb;
1126
1127 /*Skip mount point*/
1128 path += strlen(s: mp->name);
1129
1130 /*Load root*/
1131 r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, ref: &ref);
1132 if (r != EOK)
1133 return r;
1134
1135 len = ext4_path_check(path, is_goal: &is_goal);
1136 while (1) {
1137
1138 len = ext4_path_check(path, is_goal: &is_goal);
1139 if (!len) {
1140 /*If root open was request.*/
1141 r = is_goal ? EINVAL : ENOENT;
1142 break;
1143 }
1144
1145 r = ext4_dir_find_entry(result: &result, parent: &ref, name: path, name_len: len);
1146 if (r != EOK) {
1147
1148 /*Destroy last result*/
1149 ext4_dir_destroy_result(parent: &ref, result: &result);
1150
1151 if (r != ENOENT || !is_goal)
1152 break;
1153
1154 /*Link with root dir.*/
1155 r = ext4_link(mp, parent: &ref, ch: child_ref, n: path, len, rename);
1156 break;
1157 } else if (r == EOK && is_goal) {
1158 /*Destroy last result*/
1159 ext4_dir_destroy_result(parent: &ref, result: &result);
1160 r = EEXIST;
1161 break;
1162 }
1163
1164 next_inode = result.dentry->inode;
1165 if (ext4_sb_feature_incom(s: sb, EXT4_FINCOM_FILETYPE)) {
1166 uint8_t t;
1167 t = ext4_dir_en_get_inode_type(sb, de: result.dentry);
1168 inode_mode = ext4_fs_correspond_inode_mode(filetype: t);
1169 } else {
1170 struct ext4_inode_ref child_ref;
1171 r = ext4_fs_get_inode_ref(fs, index: next_inode, ref: &child_ref);
1172 if (r != EOK)
1173 break;
1174
1175 inode_mode = ext4_inode_type(sb, inode: child_ref.inode);
1176 ext4_fs_put_inode_ref(ref: &child_ref);
1177 }
1178
1179 r = ext4_dir_destroy_result(parent: &ref, result: &result);
1180 if (r != EOK)
1181 break;
1182
1183 if (inode_mode != EXT4_INODE_MODE_DIRECTORY) {
1184 r = is_goal ? EEXIST : ENOENT;
1185 break;
1186 }
1187
1188 r = ext4_fs_put_inode_ref(ref: &ref);
1189 if (r != EOK)
1190 break;
1191
1192 r = ext4_fs_get_inode_ref(fs, index: next_inode, ref: &ref);
1193 if (r != EOK)
1194 break;
1195
1196 if (is_goal)
1197 break;
1198
1199 path += len + 1;
1200 };
1201
1202 if (r != EOK) {
1203 ext4_fs_put_inode_ref(ref: &ref);
1204 return r;
1205 }
1206
1207 r = ext4_fs_put_inode_ref(ref: &ref);
1208 return r;
1209}
1210
1211static int ext4_remove_orig_reference(const char *path, uint32_t name_off,
1212 struct ext4_inode_ref *parent_ref,
1213 struct ext4_inode_ref *child_ref)
1214{
1215 bool is_goal;
1216 int r;
1217 int len;
1218 struct ext4_mountpoint *mp = ext4_get_mount(path);
1219
1220 if (!mp)
1221 return ENOENT;
1222
1223 /*Set path*/
1224 path += name_off;
1225
1226 len = ext4_path_check(path, is_goal: &is_goal);
1227
1228 /* Remove entry from parent directory */
1229 r = ext4_dir_remove_entry(parent: parent_ref, name: path, name_len: len);
1230 if (r != EOK)
1231 goto Finish;
1232
1233 if (ext4_inode_is_type(sb: &mp->fs.sb, inode: child_ref->inode,
1234 EXT4_INODE_MODE_DIRECTORY)) {
1235 ext4_fs_inode_links_count_dec(inode_ref: parent_ref);
1236 parent_ref->dirty = true;
1237 }
1238Finish:
1239 return r;
1240}
1241
1242int ext4_flink(const char *path, const char *hardlink_path)
1243{
1244 int r;
1245 ext4_file f;
1246 uint32_t name_off;
1247 bool child_loaded = false;
1248 uint32_t parent_inode, child_inode;
1249 struct ext4_mountpoint *mp = ext4_get_mount(path);
1250 struct ext4_mountpoint *target_mp = ext4_get_mount(path: hardlink_path);
1251 struct ext4_inode_ref child_ref;
1252
1253 if (!mp)
1254 return ENOENT;
1255
1256 if (mp->fs.read_only)
1257 return EROFS;
1258
1259 /* Will that happen? Anyway return EINVAL for such case. */
1260 if (mp != target_mp)
1261 return EINVAL;
1262
1263 EXT4_MP_LOCK(mp);
1264 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN,
1265 parent_inode: &parent_inode, name_off: &name_off);
1266 if (r != EOK) {
1267 EXT4_MP_UNLOCK(mp);
1268 return r;
1269 }
1270
1271 child_inode = f.inode;
1272 ext4_fclose(file: &f);
1273 ext4_trans_start(mp);
1274
1275 /*We have file to unlink. Load it.*/
1276 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: child_inode, ref: &child_ref);
1277 if (r != EOK)
1278 goto Finish;
1279
1280 child_loaded = true;
1281
1282 /* Creating hardlink for directory is not allowed. */
1283 if (ext4_inode_is_type(sb: &mp->fs.sb, inode: child_ref.inode,
1284 EXT4_INODE_MODE_DIRECTORY)) {
1285 r = EINVAL;
1286 goto Finish;
1287 }
1288
1289 r = ext4_create_hardlink(path: hardlink_path, child_ref: &child_ref, rename: false);
1290
1291Finish:
1292 if (child_loaded)
1293 ext4_fs_put_inode_ref(ref: &child_ref);
1294
1295 if (r != EOK)
1296 ext4_trans_abort(mp);
1297 else
1298 ext4_trans_stop(mp);
1299
1300 EXT4_MP_UNLOCK(mp);
1301 return r;
1302
1303}
1304
1305int ext4_frename(const char *path, const char *new_path)
1306{
1307 int r;
1308 ext4_file f;
1309 uint32_t name_off;
1310 bool parent_loaded = false, child_loaded = false;
1311 uint32_t parent_inode, child_inode;
1312 struct ext4_mountpoint *mp = ext4_get_mount(path);
1313 struct ext4_inode_ref child_ref, parent_ref;
1314
1315 if (!mp)
1316 return ENOENT;
1317
1318 if (mp->fs.read_only)
1319 return EROFS;
1320
1321 EXT4_MP_LOCK(mp);
1322
1323 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN,
1324 parent_inode: &parent_inode, name_off: &name_off);
1325 if (r != EOK) {
1326 EXT4_MP_UNLOCK(mp);
1327 return r;
1328 }
1329
1330 child_inode = f.inode;
1331 ext4_fclose(file: &f);
1332 ext4_trans_start(mp);
1333
1334 /*Load parent*/
1335 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: parent_inode, ref: &parent_ref);
1336 if (r != EOK)
1337 goto Finish;
1338
1339 parent_loaded = true;
1340
1341 /*We have file to unlink. Load it.*/
1342 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: child_inode, ref: &child_ref);
1343 if (r != EOK)
1344 goto Finish;
1345
1346 child_loaded = true;
1347
1348 r = ext4_create_hardlink(path: new_path, child_ref: &child_ref, rename: true);
1349 if (r != EOK)
1350 goto Finish;
1351
1352 r = ext4_remove_orig_reference(path, name_off, parent_ref: &parent_ref, child_ref: &child_ref);
1353 if (r != EOK)
1354 goto Finish;
1355
1356Finish:
1357 if (parent_loaded)
1358 ext4_fs_put_inode_ref(ref: &parent_ref);
1359
1360 if (child_loaded)
1361 ext4_fs_put_inode_ref(ref: &child_ref);
1362
1363 if (r != EOK)
1364 ext4_trans_abort(mp);
1365 else
1366 ext4_trans_stop(mp);
1367
1368 EXT4_MP_UNLOCK(mp);
1369 return r;
1370
1371}
1372
1373/****************************************************************************/
1374
1375int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
1376{
1377 struct ext4_mountpoint *mp = ext4_get_mount(path: mount_point);
1378
1379 if (!mp)
1380 return ENOENT;
1381
1382 *sb = &mp->fs.sb;
1383 return EOK;
1384}
1385
1386int ext4_cache_write_back(const char *path, bool on)
1387{
1388 struct ext4_mountpoint *mp = ext4_get_mount(path);
1389 int ret;
1390
1391 if (!mp)
1392 return ENOENT;
1393
1394 EXT4_MP_LOCK(mp);
1395 ret = ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: on);
1396 EXT4_MP_UNLOCK(mp);
1397 return ret;
1398}
1399
1400int ext4_cache_flush(const char *path)
1401{
1402 struct ext4_mountpoint *mp = ext4_get_mount(path);
1403 int ret;
1404
1405 if (!mp)
1406 return ENOENT;
1407
1408 EXT4_MP_LOCK(mp);
1409 ret = ext4_block_cache_flush(bdev: mp->fs.bdev);
1410 EXT4_MP_UNLOCK(mp);
1411 return ret;
1412}
1413
1414int ext4_fremove(const char *path)
1415{
1416 ext4_file f;
1417 uint32_t parent_inode;
1418 uint32_t child_inode;
1419 uint32_t name_off;
1420 bool is_goal;
1421 int r;
1422 int len;
1423 struct ext4_inode_ref child;
1424 struct ext4_inode_ref parent;
1425 struct ext4_mountpoint *mp = ext4_get_mount(path);
1426
1427 if (!mp)
1428 return ENOENT;
1429
1430 if (mp->fs.read_only)
1431 return EROFS;
1432
1433 EXT4_MP_LOCK(mp);
1434 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN,
1435 parent_inode: &parent_inode, name_off: &name_off);
1436 if (r != EOK) {
1437 EXT4_MP_UNLOCK(mp);
1438 return r;
1439 }
1440
1441 child_inode = f.inode;
1442 ext4_fclose(file: &f);
1443 ext4_trans_start(mp);
1444
1445 /*Load parent*/
1446 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: parent_inode, ref: &parent);
1447 if (r != EOK) {
1448 ext4_trans_abort(mp);
1449 EXT4_MP_UNLOCK(mp);
1450 return r;
1451 }
1452
1453 /*We have file to delete. Load it.*/
1454 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: child_inode, ref: &child);
1455 if (r != EOK) {
1456 ext4_fs_put_inode_ref(ref: &parent);
1457 ext4_trans_abort(mp);
1458 EXT4_MP_UNLOCK(mp);
1459 return r;
1460 }
1461 /* We do not allow opening files here. */
1462 if (ext4_inode_type(sb: &mp->fs.sb, inode: child.inode) ==
1463 EXT4_INODE_MODE_DIRECTORY) {
1464 ext4_fs_put_inode_ref(ref: &parent);
1465 ext4_fs_put_inode_ref(ref: &child);
1466 ext4_trans_abort(mp);
1467 EXT4_MP_UNLOCK(mp);
1468 return r;
1469 }
1470
1471 /*Link count will be zero, the inode should be freed. */
1472 if (ext4_inode_get_links_cnt(inode: child.inode) == 1) {
1473 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
1474 r = ext4_trunc_inode(mp, index: child.index, new_size: 0);
1475 if (r != EOK) {
1476 ext4_fs_put_inode_ref(ref: &parent);
1477 ext4_fs_put_inode_ref(ref: &child);
1478 ext4_trans_abort(mp);
1479 EXT4_MP_UNLOCK(mp);
1480 return r;
1481 }
1482 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
1483 }
1484
1485 /*Set path*/
1486 path += name_off;
1487
1488 len = ext4_path_check(path, is_goal: &is_goal);
1489
1490 /*Unlink from parent*/
1491 r = ext4_unlink(mp, parent: &parent, child: &child, name: path, name_len: len);
1492 if (r != EOK)
1493 goto Finish;
1494
1495 /*Link count is zero, the inode should be freed. */
1496 if (!ext4_inode_get_links_cnt(inode: child.inode)) {
1497 ext4_inode_set_del_time(inode: child.inode, time: -1L);
1498
1499 r = ext4_fs_free_inode(inode_ref: &child);
1500 if (r != EOK)
1501 goto Finish;
1502 }
1503
1504Finish:
1505 ext4_fs_put_inode_ref(ref: &child);
1506 ext4_fs_put_inode_ref(ref: &parent);
1507
1508 if (r != EOK)
1509 ext4_trans_abort(mp);
1510 else
1511 ext4_trans_stop(mp);
1512
1513 EXT4_MP_UNLOCK(mp);
1514 return r;
1515}
1516
1517int ext4_fopen(ext4_file *file, const char *path, const char *flags)
1518{
1519 struct ext4_mountpoint *mp = ext4_get_mount(path);
1520 int r;
1521
1522 if (!mp)
1523 return ENOENT;
1524
1525 EXT4_MP_LOCK(mp);
1526
1527 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
1528 r = ext4_generic_open(f: file, path, flags, file_expect: true, parent_inode: 0, name_off: 0);
1529 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
1530
1531 EXT4_MP_UNLOCK(mp);
1532 return r;
1533}
1534
1535int ext4_fopen2(ext4_file *file, const char *path, int flags)
1536{
1537 struct ext4_mountpoint *mp = ext4_get_mount(path);
1538 int r;
1539 int filetype;
1540
1541 if (!mp)
1542 return ENOENT;
1543
1544 filetype = EXT4_DE_REG_FILE;
1545
1546 EXT4_MP_LOCK(mp);
1547 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
1548
1549 if (flags & O_CREAT)
1550 ext4_trans_start(mp);
1551
1552 r = ext4_generic_open2(f: file, path, flags, ftype: filetype, NULL, NULL);
1553
1554 if (flags & O_CREAT) {
1555 if (r == EOK)
1556 ext4_trans_stop(mp);
1557 else
1558 ext4_trans_abort(mp);
1559 }
1560
1561 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
1562 EXT4_MP_UNLOCK(mp);
1563
1564 return r;
1565}
1566
1567int ext4_fclose(ext4_file *file)
1568{
1569 ext4_assert(file && file->mp);
1570
1571 file->mp = 0;
1572 file->flags = 0;
1573 file->inode = 0;
1574 file->fpos = file->fsize = 0;
1575
1576 return EOK;
1577}
1578
1579static int ext4_ftruncate_no_lock(ext4_file *file, uint64_t size)
1580{
1581 struct ext4_inode_ref ref;
1582 int r;
1583
1584
1585 r = ext4_fs_get_inode_ref(fs: &file->mp->fs, index: file->inode, ref: &ref);
1586 if (r != EOK) {
1587 EXT4_MP_UNLOCK(file->mp);
1588 return r;
1589 }
1590
1591 /*Sync file size*/
1592 file->fsize = ext4_inode_get_size(sb: &file->mp->fs.sb, inode: ref.inode);
1593 if (file->fsize <= size) {
1594 r = EOK;
1595 goto Finish;
1596 }
1597
1598 /*Start write back cache mode.*/
1599 r = ext4_block_cache_write_back(bdev: file->mp->fs.bdev, on_off: 1);
1600 if (r != EOK)
1601 goto Finish;
1602
1603 r = ext4_trunc_inode(mp: file->mp, index: ref.index, new_size: size);
1604 if (r != EOK)
1605 goto Finish;
1606
1607 file->fsize = size;
1608 if (file->fpos > size)
1609 file->fpos = size;
1610
1611 /*Stop write back cache mode*/
1612 ext4_block_cache_write_back(bdev: file->mp->fs.bdev, on_off: 0);
1613
1614 if (r != EOK)
1615 goto Finish;
1616
1617Finish:
1618 ext4_fs_put_inode_ref(ref: &ref);
1619 return r;
1620
1621}
1622
1623int ext4_ftruncate(ext4_file *f, uint64_t size)
1624{
1625 int r;
1626 ext4_assert(f && f->mp);
1627
1628 if (f->mp->fs.read_only)
1629 return EROFS;
1630
1631 if (f->flags & O_RDONLY)
1632 return EPERM;
1633
1634 EXT4_MP_LOCK(f->mp);
1635
1636 ext4_trans_start(mp: f->mp);
1637 r = ext4_ftruncate_no_lock(file: f, size);
1638 if (r != EOK)
1639 ext4_trans_abort(mp: f->mp);
1640 else
1641 ext4_trans_stop(mp: f->mp);
1642
1643 EXT4_MP_UNLOCK(f->mp);
1644 return r;
1645}
1646
1647int ext4_fread(ext4_file *file, void *buf, size_t size, size_t *rcnt)
1648{
1649 uint32_t unalg;
1650 uint32_t iblock_idx;
1651 uint32_t iblock_last;
1652 uint32_t block_size;
1653
1654 ext4_fsblk_t fblock;
1655 ext4_fsblk_t fblock_start;
1656 uint32_t fblock_count;
1657
1658 uint8_t *u8_buf = buf;
1659 int r;
1660 struct ext4_inode_ref ref;
1661
1662 ext4_assert(file && file->mp);
1663
1664 if (file->flags & O_WRONLY)
1665 return EPERM;
1666
1667 if (!size)
1668 return EOK;
1669
1670 EXT4_MP_LOCK(file->mp);
1671
1672 struct ext4_fs *const fs = &file->mp->fs;
1673 struct ext4_sblock *const sb = &file->mp->fs.sb;
1674
1675 if (rcnt)
1676 *rcnt = 0;
1677
1678 r = ext4_fs_get_inode_ref(fs, index: file->inode, ref: &ref);
1679 if (r != EOK) {
1680 EXT4_MP_UNLOCK(file->mp);
1681 return r;
1682 }
1683
1684 /*Sync file size*/
1685 file->fsize = ext4_inode_get_size(sb, inode: ref.inode);
1686
1687 block_size = ext4_sb_get_block_size(s: sb);
1688 size = ((uint64_t)size > (file->fsize - file->fpos))
1689 ? ((size_t)(file->fsize - file->fpos)) : size;
1690
1691 iblock_idx = (uint32_t)((file->fpos) / block_size);
1692 iblock_last = (uint32_t)((file->fpos + size) / block_size);
1693 unalg = (file->fpos) % block_size;
1694
1695 /*If the size of symlink is smaller than 60 bytes*/
1696 bool softlink;
1697 softlink = ext4_inode_is_type(sb, inode: ref.inode, EXT4_INODE_MODE_SOFTLINK);
1698 if (softlink && file->fsize < sizeof(ref.inode->blocks)
1699 && !ext4_inode_get_blocks_count(sb, inode: ref.inode)) {
1700
1701 char *content = (char *)ref.inode->blocks;
1702 if (file->fpos < file->fsize) {
1703 size_t len = size;
1704 if (unalg + size > (uint32_t)file->fsize)
1705 len = (uint32_t)file->fsize - unalg;
1706 memcpy(dest: buf, src: content + unalg, size: len);
1707 if (rcnt)
1708 *rcnt = len;
1709
1710 }
1711
1712 r = EOK;
1713 goto Finish;
1714 }
1715
1716 if (unalg) {
1717 size_t len = size;
1718 if (size > (block_size - unalg))
1719 len = block_size - unalg;
1720
1721 r = ext4_fs_get_inode_dblk_idx(inode_ref: &ref, iblock: iblock_idx, fblock: &fblock, support_unwritten: true);
1722 if (r != EOK)
1723 goto Finish;
1724
1725 /* Do we get an unwritten range? */
1726 if (fblock != 0) {
1727 uint64_t off = fblock * block_size + unalg;
1728 r = ext4_block_readbytes(bdev: file->mp->fs.bdev, off, buf: u8_buf, len);
1729 if (r != EOK)
1730 goto Finish;
1731
1732 } else {
1733 /* Yes, we do. */
1734 memset(dest: u8_buf, c: 0, size: len);
1735 }
1736
1737 u8_buf += len;
1738 size -= len;
1739 file->fpos += len;
1740
1741 if (rcnt)
1742 *rcnt += len;
1743
1744 iblock_idx++;
1745 }
1746
1747 fblock_start = 0;
1748 fblock_count = 0;
1749 while (size >= block_size) {
1750 while (iblock_idx < iblock_last) {
1751 r = ext4_fs_get_inode_dblk_idx(inode_ref: &ref, iblock: iblock_idx,
1752 fblock: &fblock, support_unwritten: true);
1753 if (r != EOK)
1754 goto Finish;
1755
1756 iblock_idx++;
1757
1758 if (!fblock_start)
1759 fblock_start = fblock;
1760
1761 if ((fblock_start + fblock_count) != fblock)
1762 break;
1763
1764 fblock_count++;
1765 }
1766
1767 r = ext4_blocks_get_direct(bdev: file->mp->fs.bdev, buf: u8_buf, lba: fblock_start,
1768 cnt: fblock_count);
1769 if (r != EOK)
1770 goto Finish;
1771
1772 size -= block_size * fblock_count;
1773 u8_buf += block_size * fblock_count;
1774 file->fpos += block_size * fblock_count;
1775
1776 if (rcnt)
1777 *rcnt += block_size * fblock_count;
1778
1779 fblock_start = fblock;
1780 fblock_count = 1;
1781 }
1782
1783 if (size) {
1784 uint64_t off;
1785 r = ext4_fs_get_inode_dblk_idx(inode_ref: &ref, iblock: iblock_idx, fblock: &fblock, support_unwritten: true);
1786 if (r != EOK)
1787 goto Finish;
1788
1789 off = fblock * block_size;
1790 r = ext4_block_readbytes(bdev: file->mp->fs.bdev, off, buf: u8_buf, len: size);
1791 if (r != EOK)
1792 goto Finish;
1793
1794 file->fpos += size;
1795
1796 if (rcnt)
1797 *rcnt += size;
1798 }
1799
1800Finish:
1801 ext4_fs_put_inode_ref(ref: &ref);
1802 EXT4_MP_UNLOCK(file->mp);
1803 return r;
1804}
1805
1806int ext4_fwrite(ext4_file *file, const void *buf, size_t size, size_t *wcnt)
1807{
1808 uint32_t unalg;
1809 uint32_t iblk_idx;
1810 uint32_t iblock_last;
1811 uint32_t ifile_blocks;
1812 uint32_t block_size;
1813
1814 uint32_t fblock_count;
1815 ext4_fsblk_t fblk;
1816 ext4_fsblk_t fblock_start;
1817
1818 struct ext4_inode_ref ref;
1819 const uint8_t *u8_buf = buf;
1820 int r, rr = EOK;
1821
1822 ext4_assert(file && file->mp);
1823
1824 if (file->mp->fs.read_only)
1825 return EROFS;
1826
1827 if (file->flags & O_RDONLY)
1828 return EPERM;
1829
1830 if (!size)
1831 return EOK;
1832
1833 EXT4_MP_LOCK(file->mp);
1834 ext4_trans_start(mp: file->mp);
1835
1836 struct ext4_fs *const fs = &file->mp->fs;
1837 struct ext4_sblock *const sb = &file->mp->fs.sb;
1838
1839 if (wcnt)
1840 *wcnt = 0;
1841
1842 r = ext4_fs_get_inode_ref(fs, index: file->inode, ref: &ref);
1843 if (r != EOK) {
1844 ext4_trans_abort(mp: file->mp);
1845 EXT4_MP_UNLOCK(file->mp);
1846 return r;
1847 }
1848
1849 /*Sync file size*/
1850 file->fsize = ext4_inode_get_size(sb, inode: ref.inode);
1851 block_size = ext4_sb_get_block_size(s: sb);
1852
1853 iblock_last = (uint32_t)((file->fpos + size) / block_size);
1854 iblk_idx = (uint32_t)(file->fpos / block_size);
1855 ifile_blocks = (uint32_t)((file->fsize + block_size - 1) / block_size);
1856
1857 unalg = (file->fpos) % block_size;
1858
1859 if (unalg) {
1860 size_t len = size;
1861 uint64_t off;
1862 if (size > (block_size - unalg))
1863 len = block_size - unalg;
1864
1865 r = ext4_fs_init_inode_dblk_idx(inode_ref: &ref, iblock: iblk_idx, fblock: &fblk);
1866 if (r != EOK)
1867 goto Finish;
1868
1869 off = fblk * block_size + unalg;
1870 r = ext4_block_writebytes(bdev: file->mp->fs.bdev, off, buf: u8_buf, len);
1871 if (r != EOK)
1872 goto Finish;
1873
1874 u8_buf += len;
1875 size -= len;
1876 file->fpos += len;
1877
1878 if (wcnt)
1879 *wcnt += len;
1880
1881 iblk_idx++;
1882 }
1883
1884 /*Start write back cache mode.*/
1885 r = ext4_block_cache_write_back(bdev: file->mp->fs.bdev, on_off: 1);
1886 if (r != EOK)
1887 goto Finish;
1888
1889 fblock_start = 0;
1890 fblock_count = 0;
1891 while (size >= block_size) {
1892
1893 while (iblk_idx < iblock_last) {
1894 if (iblk_idx < ifile_blocks) {
1895 r = ext4_fs_init_inode_dblk_idx(inode_ref: &ref, iblock: iblk_idx,
1896 fblock: &fblk);
1897 if (r != EOK)
1898 goto Finish;
1899 } else {
1900 rr = ext4_fs_append_inode_dblk(inode_ref: &ref, fblock: &fblk,
1901 iblock: &iblk_idx);
1902 if (rr != EOK) {
1903 /* Unable to append more blocks. But
1904 * some block might be allocated already
1905 * */
1906 break;
1907 }
1908 }
1909
1910 iblk_idx++;
1911
1912 if (!fblock_start) {
1913 fblock_start = fblk;
1914 }
1915
1916 if ((fblock_start + fblock_count) != fblk)
1917 break;
1918
1919 fblock_count++;
1920 }
1921
1922 r = ext4_blocks_set_direct(bdev: file->mp->fs.bdev, buf: u8_buf, lba: fblock_start,
1923 cnt: fblock_count);
1924 if (r != EOK)
1925 break;
1926
1927 size -= block_size * fblock_count;
1928 u8_buf += block_size * fblock_count;
1929 file->fpos += block_size * fblock_count;
1930
1931 if (wcnt)
1932 *wcnt += block_size * fblock_count;
1933
1934 fblock_start = fblk;
1935 fblock_count = 1;
1936
1937 if (rr != EOK) {
1938 /*ext4_fs_append_inode_block has failed and no
1939 * more blocks might be written. But node size
1940 * should be updated.*/
1941 r = rr;
1942 goto out_fsize;
1943 }
1944 }
1945
1946 /*Stop write back cache mode*/
1947 ext4_block_cache_write_back(bdev: file->mp->fs.bdev, on_off: 0);
1948
1949 if (r != EOK)
1950 goto Finish;
1951
1952 if (size) {
1953 uint64_t off;
1954 if (iblk_idx < ifile_blocks) {
1955 r = ext4_fs_init_inode_dblk_idx(inode_ref: &ref, iblock: iblk_idx, fblock: &fblk);
1956 if (r != EOK)
1957 goto Finish;
1958 } else {
1959 r = ext4_fs_append_inode_dblk(inode_ref: &ref, fblock: &fblk, iblock: &iblk_idx);
1960 if (r != EOK)
1961 /*Node size sholud be updated.*/
1962 goto out_fsize;
1963 }
1964
1965 off = fblk * block_size;
1966 r = ext4_block_writebytes(bdev: file->mp->fs.bdev, off, buf: u8_buf, len: size);
1967 if (r != EOK)
1968 goto Finish;
1969
1970 file->fpos += size;
1971
1972 if (wcnt)
1973 *wcnt += size;
1974 }
1975
1976out_fsize:
1977 if (file->fpos > file->fsize) {
1978 file->fsize = file->fpos;
1979 ext4_inode_set_size(inode: ref.inode, size: file->fsize);
1980 ref.dirty = true;
1981 }
1982
1983Finish:
1984 r = ext4_fs_put_inode_ref(ref: &ref);
1985
1986 if (r != EOK)
1987 ext4_trans_abort(mp: file->mp);
1988 else
1989 ext4_trans_stop(mp: file->mp);
1990
1991 EXT4_MP_UNLOCK(file->mp);
1992 return r;
1993}
1994
1995int ext4_fseek(ext4_file *file, int64_t offset, uint32_t origin)
1996{
1997 switch (origin) {
1998 case SEEK_SET:
1999 if (offset < 0 || (uint64_t)offset > file->fsize)
2000 return EINVAL;
2001
2002 file->fpos = offset;
2003 return EOK;
2004 case SEEK_CUR:
2005 if ((offset < 0 && (uint64_t)(-offset) > file->fpos) ||
2006 (offset > 0 &&
2007 (uint64_t)offset > (file->fsize - file->fpos)))
2008 return EINVAL;
2009
2010 file->fpos += offset;
2011 return EOK;
2012 case SEEK_END:
2013 if (offset < 0 || (uint64_t)offset > file->fsize)
2014 return EINVAL;
2015
2016 file->fpos = file->fsize - offset;
2017 return EOK;
2018 }
2019 return EINVAL;
2020}
2021
2022uint64_t ext4_ftell(ext4_file *file)
2023{
2024 return file->fpos;
2025}
2026
2027uint64_t ext4_fsize(ext4_file *file)
2028{
2029 return file->fsize;
2030}
2031
2032
2033static int ext4_trans_get_inode_ref(const char *path,
2034 struct ext4_mountpoint *mp,
2035 struct ext4_inode_ref *inode_ref)
2036{
2037 int r;
2038 ext4_file f;
2039
2040 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2041 if (r != EOK)
2042 return r;
2043
2044 ext4_trans_start(mp);
2045
2046 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: inode_ref);
2047 if (r != EOK) {
2048 ext4_trans_abort(mp);
2049 return r;
2050 }
2051
2052 return r;
2053}
2054
2055static int ext4_trans_put_inode_ref(struct ext4_mountpoint *mp,
2056 struct ext4_inode_ref *inode_ref)
2057{
2058 int r;
2059
2060 r = ext4_fs_put_inode_ref(ref: inode_ref);
2061 if (r != EOK)
2062 ext4_trans_abort(mp);
2063 else
2064 ext4_trans_stop(mp);
2065
2066 return r;
2067}
2068
2069
2070int ext4_raw_inode_fill(const char *path, uint32_t *ret_ino,
2071 struct ext4_inode *inode)
2072{
2073 int r;
2074 ext4_file f;
2075 struct ext4_inode_ref inode_ref;
2076 struct ext4_mountpoint *mp = ext4_get_mount(path);
2077
2078 if (!mp)
2079 return ENOENT;
2080
2081 EXT4_MP_LOCK(mp);
2082
2083 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2084 if (r != EOK) {
2085 EXT4_MP_UNLOCK(mp);
2086 return r;
2087 }
2088
2089 /*Load parent*/
2090 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2091 if (r != EOK) {
2092 EXT4_MP_UNLOCK(mp);
2093 return r;
2094 }
2095
2096 if (ret_ino)
2097 *ret_ino = f.inode;
2098
2099 memcpy(dest: inode, src: inode_ref.inode, size: sizeof(struct ext4_inode));
2100 ext4_fs_put_inode_ref(ref: &inode_ref);
2101 EXT4_MP_UNLOCK(mp);
2102
2103 return r;
2104}
2105
2106int ext4_inode_exist(const char *path, int type)
2107{
2108 int r;
2109 ext4_file f;
2110 struct ext4_mountpoint *mp = ext4_get_mount(path);
2111
2112 if (!mp)
2113 return ENOENT;
2114
2115 EXT4_MP_LOCK(mp);
2116 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: type, NULL, NULL);
2117 EXT4_MP_UNLOCK(mp);
2118
2119 return r;
2120}
2121
2122int ext4_mode_set(const char *path, uint32_t mode)
2123{
2124 int r;
2125 uint32_t orig_mode;
2126 struct ext4_inode_ref inode_ref;
2127 struct ext4_mountpoint *mp = ext4_get_mount(path);
2128
2129 if (!mp)
2130 return ENOENT;
2131
2132 if (mp->fs.read_only)
2133 return EROFS;
2134
2135 EXT4_MP_LOCK(mp);
2136
2137 r = ext4_trans_get_inode_ref(path, mp, inode_ref: &inode_ref);
2138 if (r != EOK)
2139 goto Finish;
2140
2141 orig_mode = ext4_inode_get_mode(sb: &mp->fs.sb, inode: inode_ref.inode);
2142 orig_mode &= ~0xFFF;
2143 orig_mode |= mode & 0xFFF;
2144 ext4_inode_set_mode(sb: &mp->fs.sb, inode: inode_ref.inode, mode: orig_mode);
2145
2146 inode_ref.dirty = true;
2147 r = ext4_trans_put_inode_ref(mp, inode_ref: &inode_ref);
2148
2149 Finish:
2150 EXT4_MP_UNLOCK(mp);
2151
2152 return r;
2153}
2154
2155int ext4_owner_set(const char *path, uint32_t uid, uint32_t gid)
2156{
2157 int r;
2158 struct ext4_inode_ref inode_ref;
2159 struct ext4_mountpoint *mp = ext4_get_mount(path);
2160
2161 if (!mp)
2162 return ENOENT;
2163
2164 if (mp->fs.read_only)
2165 return EROFS;
2166
2167 EXT4_MP_LOCK(mp);
2168
2169 r = ext4_trans_get_inode_ref(path, mp, inode_ref: &inode_ref);
2170 if (r != EOK)
2171 goto Finish;
2172
2173 ext4_inode_set_uid(inode: inode_ref.inode, uid);
2174 ext4_inode_set_gid(inode: inode_ref.inode, gid);
2175
2176 inode_ref.dirty = true;
2177 r = ext4_trans_put_inode_ref(mp, inode_ref: &inode_ref);
2178
2179 Finish:
2180 EXT4_MP_UNLOCK(mp);
2181
2182 return r;
2183}
2184
2185int ext4_mode_get(const char *path, uint32_t *mode)
2186{
2187 struct ext4_inode_ref inode_ref;
2188 struct ext4_mountpoint *mp = ext4_get_mount(path);
2189 ext4_file f;
2190 int r;
2191
2192 if (!mp)
2193 return ENOENT;
2194
2195 EXT4_MP_LOCK(mp);
2196
2197 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2198 if (r != EOK)
2199 goto Finish;
2200
2201 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2202 if (r != EOK)
2203 goto Finish;
2204
2205 *mode = ext4_inode_get_mode(sb: &mp->fs.sb, inode: inode_ref.inode);
2206 r = ext4_fs_put_inode_ref(ref: &inode_ref);
2207
2208 Finish:
2209 EXT4_MP_UNLOCK(mp);
2210
2211 return r;
2212}
2213
2214int ext4_owner_get(const char *path, uint32_t *uid, uint32_t *gid)
2215{
2216 struct ext4_inode_ref inode_ref;
2217 struct ext4_mountpoint *mp = ext4_get_mount(path);
2218 ext4_file f;
2219 int r;
2220
2221 if (!mp)
2222 return ENOENT;
2223
2224 EXT4_MP_LOCK(mp);
2225
2226 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2227 if (r != EOK)
2228 goto Finish;
2229
2230 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2231 if (r != EOK)
2232 goto Finish;
2233
2234 *uid = ext4_inode_get_uid(inode: inode_ref.inode);
2235 *gid = ext4_inode_get_gid(inode: inode_ref.inode);
2236 r = ext4_fs_put_inode_ref(ref: &inode_ref);
2237
2238 Finish:
2239 EXT4_MP_UNLOCK(mp);
2240
2241 return r;
2242}
2243
2244int ext4_atime_set(const char *path, uint32_t atime)
2245{
2246 struct ext4_inode_ref inode_ref;
2247 struct ext4_mountpoint *mp = ext4_get_mount(path);
2248 int r;
2249
2250 if (!mp)
2251 return ENOENT;
2252
2253 if (mp->fs.read_only)
2254 return EROFS;
2255
2256 EXT4_MP_LOCK(mp);
2257
2258 r = ext4_trans_get_inode_ref(path, mp, inode_ref: &inode_ref);
2259 if (r != EOK)
2260 goto Finish;
2261
2262 ext4_inode_set_access_time(inode: inode_ref.inode, time: atime);
2263 inode_ref.dirty = true;
2264 r = ext4_trans_put_inode_ref(mp, inode_ref: &inode_ref);
2265
2266 Finish:
2267 EXT4_MP_UNLOCK(mp);
2268
2269 return r;
2270}
2271
2272int ext4_mtime_set(const char *path, uint32_t mtime)
2273{
2274 struct ext4_inode_ref inode_ref;
2275 struct ext4_mountpoint *mp = ext4_get_mount(path);
2276 int r;
2277
2278 if (!mp)
2279 return ENOENT;
2280
2281 if (mp->fs.read_only)
2282 return EROFS;
2283
2284 EXT4_MP_LOCK(mp);
2285
2286 r = ext4_trans_get_inode_ref(path, mp, inode_ref: &inode_ref);
2287 if (r != EOK)
2288 goto Finish;
2289
2290 ext4_inode_set_modif_time(inode: inode_ref.inode, time: mtime);
2291 inode_ref.dirty = true;
2292 r = ext4_trans_put_inode_ref(mp, inode_ref: &inode_ref);
2293
2294 Finish:
2295 EXT4_MP_UNLOCK(mp);
2296
2297 return r;
2298}
2299
2300int ext4_ctime_set(const char *path, uint32_t ctime)
2301{
2302 struct ext4_inode_ref inode_ref;
2303 struct ext4_mountpoint *mp = ext4_get_mount(path);
2304 int r;
2305
2306 if (!mp)
2307 return ENOENT;
2308
2309 if (mp->fs.read_only)
2310 return EROFS;
2311
2312 EXT4_MP_LOCK(mp);
2313
2314 r = ext4_trans_get_inode_ref(path, mp, inode_ref: &inode_ref);
2315 if (r != EOK)
2316 goto Finish;
2317
2318 ext4_inode_set_change_inode_time(inode: inode_ref.inode, time: ctime);
2319 inode_ref.dirty = true;
2320 r = ext4_trans_put_inode_ref(mp, inode_ref: &inode_ref);
2321
2322 Finish:
2323 EXT4_MP_UNLOCK(mp);
2324
2325 return r;
2326}
2327
2328int ext4_atime_get(const char *path, uint32_t *atime)
2329{
2330 struct ext4_inode_ref inode_ref;
2331 struct ext4_mountpoint *mp = ext4_get_mount(path);
2332 ext4_file f;
2333 int r;
2334
2335 if (!mp)
2336 return ENOENT;
2337
2338 EXT4_MP_LOCK(mp);
2339
2340 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2341 if (r != EOK)
2342 goto Finish;
2343
2344 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2345 if (r != EOK)
2346 goto Finish;
2347
2348 *atime = ext4_inode_get_access_time(inode: inode_ref.inode);
2349 r = ext4_fs_put_inode_ref(ref: &inode_ref);
2350
2351 Finish:
2352 EXT4_MP_UNLOCK(mp);
2353
2354 return r;
2355}
2356
2357int ext4_mtime_get(const char *path, uint32_t *mtime)
2358{
2359 struct ext4_inode_ref inode_ref;
2360 struct ext4_mountpoint *mp = ext4_get_mount(path);
2361 ext4_file f;
2362 int r;
2363
2364 if (!mp)
2365 return ENOENT;
2366
2367 EXT4_MP_LOCK(mp);
2368
2369 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2370 if (r != EOK)
2371 goto Finish;
2372
2373 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2374 if (r != EOK)
2375 goto Finish;
2376
2377 *mtime = ext4_inode_get_modif_time(inode: inode_ref.inode);
2378 r = ext4_fs_put_inode_ref(ref: &inode_ref);
2379
2380 Finish:
2381 EXT4_MP_UNLOCK(mp);
2382
2383 return r;
2384}
2385
2386int ext4_ctime_get(const char *path, uint32_t *ctime)
2387{
2388 struct ext4_inode_ref inode_ref;
2389 struct ext4_mountpoint *mp = ext4_get_mount(path);
2390 ext4_file f;
2391 int r;
2392
2393 if (!mp)
2394 return ENOENT;
2395
2396 EXT4_MP_LOCK(mp);
2397
2398 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2399 if (r != EOK)
2400 goto Finish;
2401
2402 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: f.inode, ref: &inode_ref);
2403 if (r != EOK)
2404 goto Finish;
2405
2406 *ctime = ext4_inode_get_change_inode_time(inode: inode_ref.inode);
2407 r = ext4_fs_put_inode_ref(ref: &inode_ref);
2408
2409 Finish:
2410 EXT4_MP_UNLOCK(mp);
2411
2412 return r;
2413}
2414
2415static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
2416{
2417 struct ext4_inode_ref ref;
2418 uint32_t sblock;
2419 ext4_fsblk_t fblock;
2420 uint32_t block_size;
2421 int r;
2422
2423 ext4_assert(f && f->mp);
2424
2425 if (!size)
2426 return EOK;
2427
2428 r = ext4_fs_get_inode_ref(fs: &f->mp->fs, index: f->inode, ref: &ref);
2429 if (r != EOK)
2430 return r;
2431
2432 /*Sync file size*/
2433 block_size = ext4_sb_get_block_size(s: &f->mp->fs.sb);
2434 if (size > block_size) {
2435 r = EINVAL;
2436 goto Finish;
2437 }
2438 r = ext4_ftruncate_no_lock(file: f, size: 0);
2439 if (r != EOK)
2440 goto Finish;
2441
2442 /*Start write back cache mode.*/
2443 r = ext4_block_cache_write_back(bdev: f->mp->fs.bdev, on_off: 1);
2444 if (r != EOK)
2445 goto Finish;
2446
2447 /*If the size of symlink is smaller than 60 bytes*/
2448 if (size < sizeof(ref.inode->blocks)) {
2449 memset(dest: ref.inode->blocks, c: 0, size: sizeof(ref.inode->blocks));
2450 memcpy(dest: ref.inode->blocks, src: buf, size);
2451 ext4_inode_clear_flag(inode: ref.inode, EXT4_INODE_FLAG_EXTENTS);
2452 } else {
2453 uint64_t off;
2454 ext4_fs_inode_blocks_init(fs: &f->mp->fs, inode_ref: &ref);
2455 r = ext4_fs_append_inode_dblk(inode_ref: &ref, fblock: &fblock, iblock: &sblock);
2456 if (r != EOK)
2457 goto Finish;
2458
2459 off = fblock * block_size;
2460 r = ext4_block_writebytes(bdev: f->mp->fs.bdev, off, buf, len: size);
2461 if (r != EOK)
2462 goto Finish;
2463 }
2464
2465 /*Stop write back cache mode*/
2466 ext4_block_cache_write_back(bdev: f->mp->fs.bdev, on_off: 0);
2467
2468 if (r != EOK)
2469 goto Finish;
2470
2471 ext4_inode_set_size(inode: ref.inode, size);
2472 ref.dirty = true;
2473
2474 f->fsize = size;
2475 if (f->fpos > size)
2476 f->fpos = size;
2477
2478Finish:
2479 ext4_fs_put_inode_ref(ref: &ref);
2480 return r;
2481}
2482
2483int ext4_fsymlink(const char *target, const char *path)
2484{
2485 struct ext4_mountpoint *mp = ext4_get_mount(path);
2486 int r;
2487 ext4_file f;
2488 int filetype;
2489
2490 if (!mp)
2491 return ENOENT;
2492
2493 if (mp->fs.read_only)
2494 return EROFS;
2495
2496 filetype = EXT4_DE_SYMLINK;
2497
2498 EXT4_MP_LOCK(mp);
2499 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
2500 ext4_trans_start(mp);
2501
2502 r = ext4_generic_open2(f: &f, path, O_RDWR | O_CREAT, ftype: filetype, NULL, NULL);
2503 if (r == EOK)
2504 r = ext4_fsymlink_set(f: &f, buf: target, size: strlen(s: target));
2505 else
2506 goto Finish;
2507
2508 ext4_fclose(file: &f);
2509
2510Finish:
2511 if (r != EOK)
2512 ext4_trans_abort(mp);
2513 else
2514 ext4_trans_stop(mp);
2515
2516 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
2517 EXT4_MP_UNLOCK(mp);
2518 return r;
2519}
2520
2521int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
2522{
2523 struct ext4_mountpoint *mp = ext4_get_mount(path);
2524 int r;
2525 ext4_file f;
2526 int filetype;
2527
2528 if (!mp)
2529 return ENOENT;
2530
2531 if (!buf)
2532 return EINVAL;
2533
2534 filetype = EXT4_DE_SYMLINK;
2535
2536 EXT4_MP_LOCK(mp);
2537 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
2538 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: filetype, NULL, NULL);
2539 if (r == EOK)
2540 r = ext4_fread(file: &f, buf, size: bufsize, rcnt);
2541 else
2542 goto Finish;
2543
2544 ext4_fclose(file: &f);
2545
2546Finish:
2547 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
2548 EXT4_MP_UNLOCK(mp);
2549 return r;
2550}
2551
2552static int ext4_mknod_set(ext4_file *f, uint32_t dev)
2553{
2554 struct ext4_inode_ref ref;
2555 int r;
2556
2557 ext4_assert(f && f->mp);
2558
2559 r = ext4_fs_get_inode_ref(fs: &f->mp->fs, index: f->inode, ref: &ref);
2560 if (r != EOK)
2561 return r;
2562
2563 ext4_inode_set_dev(inode: ref.inode, dev);
2564
2565 ext4_inode_set_size(inode: ref.inode, size: 0);
2566 ref.dirty = true;
2567
2568 f->fsize = 0;
2569 f->fpos = 0;
2570
2571 r = ext4_fs_put_inode_ref(ref: &ref);
2572 return r;
2573}
2574
2575int ext4_mknod(const char *path, int filetype, uint32_t dev)
2576{
2577 struct ext4_mountpoint *mp = ext4_get_mount(path);
2578 int r;
2579 ext4_file f;
2580
2581 if (!mp)
2582 return ENOENT;
2583
2584 if (mp->fs.read_only)
2585 return EROFS;
2586
2587 /*
2588 * The filetype shouldn't be normal file, directory or
2589 * unknown.
2590 */
2591 if (filetype == EXT4_DE_UNKNOWN ||
2592 filetype == EXT4_DE_REG_FILE ||
2593 filetype == EXT4_DE_DIR ||
2594 filetype == EXT4_DE_SYMLINK)
2595 return EINVAL;
2596
2597 /*
2598 * Nor should it be any bogus value.
2599 */
2600 if (filetype != EXT4_DE_CHRDEV &&
2601 filetype != EXT4_DE_BLKDEV &&
2602 filetype != EXT4_DE_FIFO &&
2603 filetype != EXT4_DE_SOCK)
2604 return EINVAL;
2605
2606 EXT4_MP_LOCK(mp);
2607 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
2608 ext4_trans_start(mp);
2609
2610 r = ext4_generic_open2(f: &f, path, O_RDWR | O_CREAT, ftype: filetype, NULL, NULL);
2611 if (r == EOK) {
2612 if (filetype == EXT4_DE_CHRDEV ||
2613 filetype == EXT4_DE_BLKDEV)
2614 r = ext4_mknod_set(f: &f, dev);
2615 } else {
2616 goto Finish;
2617 }
2618
2619 ext4_fclose(file: &f);
2620
2621Finish:
2622 if (r != EOK)
2623 ext4_trans_abort(mp);
2624 else
2625 ext4_trans_stop(mp);
2626
2627 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
2628 EXT4_MP_UNLOCK(mp);
2629 return r;
2630}
2631
2632int ext4_setxattr(const char *path, const char *name, size_t name_len,
2633 const void *data, size_t data_size)
2634{
2635 bool found;
2636 int r = EOK;
2637 ext4_file f;
2638 uint32_t inode;
2639 uint8_t name_index;
2640 const char *dissected_name = NULL;
2641 size_t dissected_len = 0;
2642 struct ext4_inode_ref inode_ref;
2643 struct ext4_mountpoint *mp = ext4_get_mount(path);
2644 if (!mp)
2645 return ENOENT;
2646
2647 if (mp->fs.read_only)
2648 return EROFS;
2649
2650 dissected_name = ext4_extract_xattr_name(full_name: name, full_name_len: name_len,
2651 name_index: &name_index, name_len: &dissected_len,
2652 found: &found);
2653 if (!found)
2654 return EINVAL;
2655
2656 EXT4_MP_LOCK(mp);
2657 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2658 if (r != EOK) {
2659 EXT4_MP_UNLOCK(mp);
2660 return r;
2661 }
2662
2663 inode = f.inode;
2664 ext4_fclose(file: &f);
2665 ext4_trans_start(mp);
2666
2667 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: inode, ref: &inode_ref);
2668 if (r != EOK)
2669 goto Finish;
2670
2671 r = ext4_xattr_set(inode_ref: &inode_ref, name_index, name: dissected_name,
2672 name_len: dissected_len, value: data, value_len: data_size);
2673
2674 ext4_fs_put_inode_ref(ref: &inode_ref);
2675Finish:
2676 if (r != EOK)
2677 ext4_trans_abort(mp);
2678 else
2679 ext4_trans_stop(mp);
2680
2681 EXT4_MP_UNLOCK(mp);
2682 return r;
2683}
2684
2685int ext4_getxattr(const char *path, const char *name, size_t name_len,
2686 void *buf, size_t buf_size, size_t *data_size)
2687{
2688 bool found;
2689 int r = EOK;
2690 ext4_file f;
2691 uint32_t inode;
2692 uint8_t name_index;
2693 const char *dissected_name = NULL;
2694 size_t dissected_len = 0;
2695 struct ext4_inode_ref inode_ref;
2696 struct ext4_mountpoint *mp = ext4_get_mount(path);
2697 if (!mp)
2698 return ENOENT;
2699
2700 dissected_name = ext4_extract_xattr_name(full_name: name, full_name_len: name_len,
2701 name_index: &name_index, name_len: &dissected_len,
2702 found: &found);
2703 if (!found)
2704 return EINVAL;
2705
2706 EXT4_MP_LOCK(mp);
2707 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2708 if (r != EOK)
2709 goto Finish;
2710
2711 inode = f.inode;
2712 ext4_fclose(file: &f);
2713
2714 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: inode, ref: &inode_ref);
2715 if (r != EOK)
2716 goto Finish;
2717
2718 r = ext4_xattr_get(inode_ref: &inode_ref, name_index, name: dissected_name,
2719 name_len: dissected_len, buf, buf_len: buf_size, data_len: data_size);
2720
2721 ext4_fs_put_inode_ref(ref: &inode_ref);
2722Finish:
2723 EXT4_MP_UNLOCK(mp);
2724 return r;
2725}
2726
2727int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
2728{
2729 int r = EOK;
2730 ext4_file f;
2731 uint32_t inode;
2732 size_t list_len, list_size = 0;
2733 struct ext4_inode_ref inode_ref;
2734 struct ext4_xattr_list_entry *xattr_list = NULL,
2735 *entry = NULL;
2736 struct ext4_mountpoint *mp = ext4_get_mount(path);
2737 if (!mp)
2738 return ENOENT;
2739
2740 EXT4_MP_LOCK(mp);
2741 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2742 if (r != EOK)
2743 goto Finish;
2744 inode = f.inode;
2745 ext4_fclose(file: &f);
2746
2747 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: inode, ref: &inode_ref);
2748 if (r != EOK)
2749 goto Finish;
2750
2751 r = ext4_xattr_list(inode_ref: &inode_ref, NULL, list_len: &list_len);
2752 if (r == EOK && list_len) {
2753 xattr_list = ext4_malloc(size: list_len);
2754 if (!xattr_list) {
2755 ext4_fs_put_inode_ref(ref: &inode_ref);
2756 r = ENOMEM;
2757 goto Finish;
2758 }
2759 entry = xattr_list;
2760 r = ext4_xattr_list(inode_ref: &inode_ref, list: entry, list_len: &list_len);
2761 if (r != EOK) {
2762 ext4_fs_put_inode_ref(ref: &inode_ref);
2763 goto Finish;
2764 }
2765
2766 for (;entry;entry = entry->next) {
2767 size_t prefix_len;
2768 const char *prefix =
2769 ext4_get_xattr_name_prefix(name_index: entry->name_index,
2770 ret_prefix_len: &prefix_len);
2771 if (size) {
2772 if (prefix_len + entry->name_len + 1 > size) {
2773 ext4_fs_put_inode_ref(ref: &inode_ref);
2774 r = ERANGE;
2775 goto Finish;
2776 }
2777 }
2778
2779 if (list && size) {
2780 memcpy(dest: list, src: prefix, size: prefix_len);
2781 list += prefix_len;
2782 memcpy(dest: list, src: entry->name,
2783 size: entry->name_len);
2784 list[entry->name_len] = 0;
2785 list += entry->name_len + 1;
2786
2787 size -= prefix_len + entry->name_len + 1;
2788 }
2789
2790 list_size += prefix_len + entry->name_len + 1;
2791 }
2792 if (ret_size)
2793 *ret_size = list_size;
2794
2795 }
2796 ext4_fs_put_inode_ref(ref: &inode_ref);
2797Finish:
2798 EXT4_MP_UNLOCK(mp);
2799 if (xattr_list)
2800 ext4_free(pointer: xattr_list);
2801
2802 return r;
2803
2804}
2805
2806int ext4_removexattr(const char *path, const char *name, size_t name_len)
2807{
2808 bool found;
2809 int r = EOK;
2810 ext4_file f;
2811 uint32_t inode;
2812 uint8_t name_index;
2813 const char *dissected_name = NULL;
2814 size_t dissected_len = 0;
2815 struct ext4_inode_ref inode_ref;
2816 struct ext4_mountpoint *mp = ext4_get_mount(path);
2817 if (!mp)
2818 return ENOENT;
2819
2820 if (mp->fs.read_only)
2821 return EROFS;
2822
2823 dissected_name = ext4_extract_xattr_name(full_name: name, full_name_len: name_len,
2824 name_index: &name_index, name_len: &dissected_len,
2825 found: &found);
2826 if (!found)
2827 return EINVAL;
2828
2829 EXT4_MP_LOCK(mp);
2830 r = ext4_generic_open2(f: &f, path, O_RDONLY, ftype: EXT4_DE_UNKNOWN, NULL, NULL);
2831 if (r != EOK) {
2832 EXT4_MP_LOCK(mp);
2833 return r;
2834 }
2835
2836 inode = f.inode;
2837 ext4_fclose(file: &f);
2838 ext4_trans_start(mp);
2839
2840 r = ext4_fs_get_inode_ref(fs: &mp->fs, index: inode, ref: &inode_ref);
2841 if (r != EOK)
2842 goto Finish;
2843
2844 r = ext4_xattr_remove(inode_ref: &inode_ref, name_index, name: dissected_name,
2845 name_len: dissected_len);
2846
2847 ext4_fs_put_inode_ref(ref: &inode_ref);
2848Finish:
2849 if (r != EOK)
2850 ext4_trans_abort(mp);
2851 else
2852 ext4_trans_stop(mp);
2853
2854 EXT4_MP_UNLOCK(mp);
2855 return r;
2856
2857}
2858
2859/*********************************DIRECTORY OPERATION************************/
2860
2861int ext4_dir_rm(const char *path)
2862{
2863 int r;
2864 int len;
2865 ext4_file f;
2866
2867 struct ext4_mountpoint *mp = ext4_get_mount(path);
2868 struct ext4_inode_ref act;
2869 struct ext4_inode_ref child;
2870 struct ext4_dir_iter it;
2871
2872 uint32_t name_off;
2873 uint32_t inode_up;
2874 uint32_t inode_current;
2875 uint32_t depth = 1;
2876
2877 bool has_children;
2878 bool is_goal;
2879 bool dir_end;
2880
2881 if (!mp)
2882 return ENOENT;
2883
2884 if (mp->fs.read_only)
2885 return EROFS;
2886
2887 EXT4_MP_LOCK(mp);
2888
2889 struct ext4_fs *const fs = &mp->fs;
2890
2891 /*Check if exist.*/
2892 r = ext4_generic_open(f: &f, path, flags: "r", file_expect: false, parent_inode: &inode_up, name_off: &name_off);
2893 if (r != EOK) {
2894 EXT4_MP_UNLOCK(mp);
2895 return r;
2896 }
2897
2898 path += name_off;
2899 len = ext4_path_check(path, is_goal: &is_goal);
2900 inode_current = f.inode;
2901
2902 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 1);
2903
2904 do {
2905
2906 uint64_t act_curr_pos = 0;
2907 has_children = false;
2908 dir_end = false;
2909
2910 while (r == EOK && !has_children && !dir_end) {
2911
2912 /*Load directory node.*/
2913 r = ext4_fs_get_inode_ref(fs, index: inode_current, ref: &act);
2914 if (r != EOK) {
2915 break;
2916 }
2917
2918 /*Initialize iterator.*/
2919 r = ext4_dir_iterator_init(it: &it, inode_ref: &act, pos: act_curr_pos);
2920 if (r != EOK) {
2921 ext4_fs_put_inode_ref(ref: &act);
2922 break;
2923 }
2924
2925 if (!it.curr) {
2926 dir_end = true;
2927 goto End;
2928 }
2929
2930 ext4_trans_start(mp);
2931
2932 /*Get up directory inode when ".." entry*/
2933 if ((it.curr->name_len == 2) &&
2934 ext4_is_dots(name: it.curr->name, name_size: it.curr->name_len)) {
2935 inode_up = ext4_dir_en_get_inode(de: it.curr);
2936 }
2937
2938 /*If directory or file entry, but not "." ".." entry*/
2939 if (!ext4_is_dots(name: it.curr->name, name_size: it.curr->name_len)) {
2940
2941 /*Get child inode reference do unlink
2942 * directory/file.*/
2943 uint32_t cinode;
2944 uint32_t inode_type;
2945 cinode = ext4_dir_en_get_inode(de: it.curr);
2946 r = ext4_fs_get_inode_ref(fs, index: cinode, ref: &child);
2947 if (r != EOK)
2948 goto End;
2949
2950 /*If directory with no leaf children*/
2951 r = ext4_has_children(has_children: &has_children, enode: &child);
2952 if (r != EOK) {
2953 ext4_fs_put_inode_ref(ref: &child);
2954 goto End;
2955 }
2956
2957 if (has_children) {
2958 /*Has directory children. Go into this
2959 * directory.*/
2960 inode_up = inode_current;
2961 inode_current = cinode;
2962 depth++;
2963 ext4_fs_put_inode_ref(ref: &child);
2964 goto End;
2965 }
2966 inode_type = ext4_inode_type(sb: &mp->fs.sb,
2967 inode: child.inode);
2968
2969 /* Truncate */
2970 if (inode_type != EXT4_INODE_MODE_DIRECTORY)
2971 r = ext4_trunc_inode(mp, index: child.index, new_size: 0);
2972 else
2973 r = ext4_trunc_dir(mp, parent: &act, dir: &child);
2974
2975 if (r != EOK) {
2976 ext4_fs_put_inode_ref(ref: &child);
2977 goto End;
2978 }
2979
2980 /*No children in child directory or file. Just
2981 * unlink.*/
2982 r = ext4_unlink(mp: f.mp, parent: &act, child: &child,
2983 name: (char *)it.curr->name,
2984 name_len: it.curr->name_len);
2985 if (r != EOK) {
2986 ext4_fs_put_inode_ref(ref: &child);
2987 goto End;
2988 }
2989
2990 ext4_inode_set_del_time(inode: child.inode, time: -1L);
2991 ext4_inode_set_links_cnt(inode: child.inode, cnt: 0);
2992 child.dirty = true;
2993
2994 r = ext4_fs_free_inode(inode_ref: &child);
2995 if (r != EOK) {
2996 ext4_fs_put_inode_ref(ref: &child);
2997 goto End;
2998 }
2999
3000 r = ext4_fs_put_inode_ref(ref: &child);
3001 if (r != EOK)
3002 goto End;
3003
3004 }
3005
3006 r = ext4_dir_iterator_next(it: &it);
3007 if (r != EOK)
3008 goto End;
3009
3010 act_curr_pos = it.curr_off;
3011End:
3012 ext4_dir_iterator_fini(it: &it);
3013 if (r == EOK)
3014 r = ext4_fs_put_inode_ref(ref: &act);
3015 else
3016 ext4_fs_put_inode_ref(ref: &act);
3017
3018 if (r != EOK)
3019 ext4_trans_abort(mp);
3020 else
3021 ext4_trans_stop(mp);
3022 }
3023
3024 if (dir_end) {
3025 /*Directory iterator reached last entry*/
3026 depth--;
3027 if (depth)
3028 inode_current = inode_up;
3029
3030 }
3031
3032 if (r != EOK)
3033 break;
3034
3035 } while (depth);
3036
3037 /*Last unlink*/
3038 if (r == EOK && !depth) {
3039 /*Load parent.*/
3040 struct ext4_inode_ref parent;
3041 r = ext4_fs_get_inode_ref(fs: &f.mp->fs, index: inode_up,
3042 ref: &parent);
3043 if (r != EOK)
3044 goto Finish;
3045 r = ext4_fs_get_inode_ref(fs: &f.mp->fs, index: inode_current,
3046 ref: &act);
3047 if (r != EOK) {
3048 ext4_fs_put_inode_ref(ref: &act);
3049 goto Finish;
3050 }
3051
3052 ext4_trans_start(mp);
3053
3054 /* In this place all directories should be
3055 * unlinked.
3056 * Last unlink from root of current directory*/
3057 r = ext4_unlink(mp: f.mp, parent: &parent, child: &act,
3058 name: (char *)path, name_len: len);
3059 if (r != EOK) {
3060 ext4_fs_put_inode_ref(ref: &parent);
3061 ext4_fs_put_inode_ref(ref: &act);
3062 goto Finish;
3063 }
3064
3065 if (ext4_inode_get_links_cnt(inode: act.inode) == 2) {
3066 ext4_inode_set_del_time(inode: act.inode, time: -1L);
3067 ext4_inode_set_links_cnt(inode: act.inode, cnt: 0);
3068 act.dirty = true;
3069 /*Truncate*/
3070 r = ext4_trunc_dir(mp, parent: &parent, dir: &act);
3071 if (r != EOK) {
3072 ext4_fs_put_inode_ref(ref: &parent);
3073 ext4_fs_put_inode_ref(ref: &act);
3074 goto Finish;
3075 }
3076
3077 r = ext4_fs_free_inode(inode_ref: &act);
3078 if (r != EOK) {
3079 ext4_fs_put_inode_ref(ref: &parent);
3080 ext4_fs_put_inode_ref(ref: &act);
3081 goto Finish;
3082 }
3083 }
3084
3085 r = ext4_fs_put_inode_ref(ref: &parent);
3086 if (r != EOK)
3087 goto Finish;
3088
3089 r = ext4_fs_put_inode_ref(ref: &act);
3090 Finish:
3091 if (r != EOK)
3092 ext4_trans_abort(mp);
3093 else
3094 ext4_trans_stop(mp);
3095 }
3096
3097 ext4_block_cache_write_back(bdev: mp->fs.bdev, on_off: 0);
3098 EXT4_MP_UNLOCK(mp);
3099
3100 return r;
3101}
3102
3103int ext4_dir_mv(const char *path, const char *new_path)
3104{
3105 return ext4_frename(path, new_path);
3106}
3107
3108int ext4_dir_mk(const char *path)
3109{
3110 int r;
3111 ext4_file f;
3112 struct ext4_mountpoint *mp = ext4_get_mount(path);
3113
3114 if (!mp)
3115 return ENOENT;
3116
3117 if (mp->fs.read_only)
3118 return EROFS;
3119
3120 EXT4_MP_LOCK(mp);
3121
3122 /*Check if exist.*/
3123 r = ext4_generic_open(f: &f, path, flags: "r", file_expect: false, parent_inode: 0, name_off: 0);
3124 if (r == EOK)
3125 goto Finish;
3126
3127 /*Create new directory.*/
3128 r = ext4_generic_open(f: &f, path, flags: "w", file_expect: false, parent_inode: 0, name_off: 0);
3129
3130Finish:
3131 EXT4_MP_UNLOCK(mp);
3132 return r;
3133}
3134
3135int ext4_dir_open(ext4_dir *dir, const char *path)
3136{
3137 struct ext4_mountpoint *mp = ext4_get_mount(path);
3138 int r;
3139
3140 if (!mp)
3141 return ENOENT;
3142
3143 EXT4_MP_LOCK(mp);
3144 r = ext4_generic_open(f: &dir->f, path, flags: "r", file_expect: false, parent_inode: 0, name_off: 0);
3145 dir->next_off = 0;
3146 EXT4_MP_UNLOCK(mp);
3147 return r;
3148}
3149
3150int ext4_dir_close(ext4_dir *dir)
3151{
3152 return ext4_fclose(file: &dir->f);
3153}
3154
3155const ext4_direntry *ext4_dir_entry_next(ext4_dir *dir)
3156{
3157#define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
3158
3159 int r;
3160 uint16_t name_length;
3161 ext4_direntry *de = 0;
3162 struct ext4_inode_ref dir_inode;
3163 struct ext4_dir_iter it;
3164
3165 EXT4_MP_LOCK(dir->f.mp);
3166
3167 if (dir->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
3168 EXT4_MP_UNLOCK(dir->f.mp);
3169 return 0;
3170 }
3171
3172 r = ext4_fs_get_inode_ref(fs: &dir->f.mp->fs, index: dir->f.inode, ref: &dir_inode);
3173 if (r != EOK) {
3174 goto Finish;
3175 }
3176
3177 r = ext4_dir_iterator_init(it: &it, inode_ref: &dir_inode, pos: dir->next_off);
3178 if (r != EOK) {
3179 ext4_fs_put_inode_ref(ref: &dir_inode);
3180 goto Finish;
3181 }
3182
3183 memset(dest: &dir->de.name, c: 0, size: sizeof(dir->de.name));
3184 name_length = ext4_dir_en_get_name_len(sb: &dir->f.mp->fs.sb,
3185 de: it.curr);
3186 memcpy(dest: &dir->de.name, src: it.curr->name, size: name_length);
3187
3188 /* Directly copying the content isn't safe for Big-endian targets*/
3189 dir->de.inode = ext4_dir_en_get_inode(de: it.curr);
3190 dir->de.entry_length = ext4_dir_en_get_entry_len(de: it.curr);
3191 dir->de.name_length = name_length;
3192 dir->de.inode_type = ext4_dir_en_get_inode_type(sb: &dir->f.mp->fs.sb,
3193 de: it.curr);
3194
3195 de = &dir->de;
3196
3197 ext4_dir_iterator_next(it: &it);
3198
3199 dir->next_off = it.curr ? it.curr_off : EXT4_DIR_ENTRY_OFFSET_TERM;
3200
3201 ext4_dir_iterator_fini(it: &it);
3202 ext4_fs_put_inode_ref(ref: &dir_inode);
3203
3204Finish:
3205 EXT4_MP_UNLOCK(dir->f.mp);
3206 return de;
3207}
3208
3209void ext4_dir_entry_rewind(ext4_dir *dir)
3210{
3211 dir->next_off = 0;
3212}
3213
3214/**
3215 * @}
3216 */
3217