1#include "fs/ext2/tests/test_internal.h"
2
3/* ext2 file type is a 4 bit enumerated field in the top nibble of mode */
4struct ftype_case {
5 uint16_t mode;
6 uint8_t expect;
7 const char *name;
8};
9
10static const struct ftype_case ftype_cases[] = {
11 {EXT2_S_IFREG, EXT2_FT_REG_FILE, "regular"},
12 {EXT2_S_IFDIR, EXT2_FT_DIR, "directory"},
13 {EXT2_S_IFCHR, EXT2_FT_CHRDEV, "chardev"},
14 {EXT2_S_IFBLK, EXT2_FT_BLKDEV, "blockdev"},
15 {EXT2_S_IFIFO, EXT2_FT_FIFO, "fifo"},
16 {EXT2_S_IFSOCK, EXT2_FT_SOCK, "socket"},
17 {EXT2_S_IFLNK, EXT2_FT_SYMLINK, "symlink"},
18};
19
20TEST_DEFINE_UNIT(ext2, ftype_all_types) {
21 for (size_t i = 0; i < TEST_ARRAY_LEN(ftype_cases); i++) {
22 const struct ftype_case *c = &ftype_cases[i];
23 uint8_t got = ext2_extract_ftype(mode: c->mode);
24
25 if (got != c->expect) {
26 test_err("ftype(%s / %04x) = %u, want %u", c->name, c->mode, got,
27 c->expect);
28 return TEST_FAIL("ext2_extract_ftype");
29 }
30 }
31
32 return TEST_SUCCESS;
33}
34
35/* Perm bits share word with type */
36TEST_DEFINE_UNIT(ext2, ftype_ignores_permissions) {
37 const uint16_t perms = 0x0FFF;
38
39 for (size_t i = 0; i < TEST_ARRAY_LEN(ftype_cases); i++) {
40 const struct ftype_case *c = &ftype_cases[i];
41 TEST_ASSERT_EQ(ext2_extract_ftype(c->mode | perms), c->expect);
42 TEST_ASSERT_EQ(ext2_extract_ftype(c->mode), c->expect);
43 }
44
45 return TEST_SUCCESS;
46}
47
48TEST_DEFINE_UNIT(ext2, ftype_unknown) {
49 /* Zero type field is not any of the seven, must not be guessed */
50 TEST_ASSERT_EQ(ext2_extract_ftype(0), EXT2_FT_UNKNOWN);
51 TEST_ASSERT_EQ(ext2_extract_ftype(0x0FFF), EXT2_FT_UNKNOWN);
52
53 /* Each type maps to a distinct value in the defined range */
54 for (size_t i = 0; i < TEST_ARRAY_LEN(ftype_cases); i++) {
55 TEST_ASSERT_LT(ftype_cases[i].expect, EXT2_FT_MAX);
56 for (size_t j = i + 1; j < TEST_ARRAY_LEN(ftype_cases); j++)
57 TEST_ASSERT_NE(ftype_cases[i].expect, ftype_cases[j].expect);
58 }
59
60 return TEST_SUCCESS;
61}
62
63TEST_DEFINE_UNIT(ext2, mode_type_roundtrip) {
64 for (size_t i = 0; i < TEST_ARRAY_LEN(ftype_cases); i++) {
65 uint16_t ext2 = ftype_cases[i].mode;
66 uint16_t vfs = TEST_CALL(ext2_to_vfs_mode)(ext2_mode: ext2);
67
68 TEST_ASSERT_EQ((TEST_CALL(vfs_to_ext2_mode)(vfs) & EXT2_S_IFMT),
69 (ext2 & EXT2_S_IFMT));
70 }
71
72 return TEST_SUCCESS;
73}
74
75TEST_DEFINE_UNIT(ext2, mode_permission_roundtrip) {
76 static const uint16_t perm_bits[] = {
77 EXT2_S_IRUSR, EXT2_S_IWUSR, EXT2_S_IXUSR, EXT2_S_IRGRP, EXT2_S_IWGRP,
78 EXT2_S_IXGRP, EXT2_S_IROTH, EXT2_S_IWOTH, EXT2_S_IXOTH,
79 };
80
81 /* One bit at a time isolates a swapped owner/group/other mappings, which
82 * 0777 round trip would hide away */
83 for (size_t i = 0; i < TEST_ARRAY_LEN(perm_bits); i++) {
84 uint16_t ext2 = EXT2_S_IFREG | perm_bits[i];
85 uint16_t back =
86 TEST_CALL(vfs_to_ext2_mode)(TEST_CALL(ext2_to_vfs_mode)(ext2_mode: ext2));
87
88 TEST_ASSERT_EQ(back, ext2);
89 }
90
91 /* full set together */
92 uint16_t all = EXT2_S_IFREG;
93 for (size_t i = 0; i < TEST_ARRAY_LEN(perm_bits); i++)
94 all |= perm_bits[i];
95
96 TEST_ASSERT_EQ(
97 TEST_CALL(vfs_to_ext2_mode)(TEST_CALL(ext2_to_vfs_mode)(all)), all);
98
99 return TEST_SUCCESS;
100}
101
102TEST_DEFINE_UNIT(ext2, mode_flags_roundtrip) {
103 static const uint32_t flags[] = {
104 EXT2_APPEND_FL, EXT2_IMMUTABLE_FL, EXT2_NOATIME_FL,
105 EXT2_SYNC_FL, EXT2_DIRSYNC_FL,
106 };
107
108 uint32_t all = 0;
109 for (size_t i = 0; i < TEST_ARRAY_LEN(flags); i++) {
110 uint32_t back = TEST_CALL(vfs_to_ext2_flags)(
111 TEST_CALL(ext2_to_vfs_flags)(ext2_flags: flags[i]));
112
113 TEST_ASSERT_EQ(back, flags[i]);
114 all |= flags[i];
115 }
116
117 TEST_ASSERT_EQ(
118 TEST_CALL(vfs_to_ext2_flags)(TEST_CALL(ext2_to_vfs_flags)(all)), all);
119
120 TEST_ASSERT_EQ(TEST_CALL(ext2_to_vfs_flags)(0), 0);
121 TEST_ASSERT_EQ(TEST_CALL(vfs_to_ext2_flags)(0), 0);
122
123 /* Flags we don't model mustn't be dropped */
124 TEST_ASSERT_EQ(
125 (TEST_CALL(vfs_to_ext2_flags)(TEST_CALL(ext2_to_vfs_flags)(~all)) &
126 all),
127 0);
128
129 return TEST_SUCCESS;
130}
131