/src/u-boot/fs/erofs/namei.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | #include "internal.h" |
3 | | |
4 | 0 | #define makedev(major, minor) ((dev_t)((((major) & 0xfff) << 8) | ((minor) & 0xff))) |
5 | | static dev_t erofs_new_decode_dev(u32 dev) |
6 | 0 | { |
7 | 0 | const unsigned int major = (dev & 0xfff00) >> 8; |
8 | 0 | const unsigned int minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); |
9 | |
|
10 | 0 | return makedev(major, minor); |
11 | 0 | } |
12 | | |
13 | | int erofs_read_inode_from_disk(struct erofs_inode *vi) |
14 | 0 | { |
15 | 0 | int ret, ifmt; |
16 | 0 | char buf[sizeof(struct erofs_inode_extended)]; |
17 | 0 | struct erofs_inode_compact *dic; |
18 | 0 | struct erofs_inode_extended *die; |
19 | 0 | const erofs_off_t inode_loc = iloc(vi->nid); |
20 | |
|
21 | 0 | ret = erofs_dev_read(0, buf, inode_loc, sizeof(*dic)); |
22 | 0 | if (ret < 0) |
23 | 0 | return -EIO; |
24 | | |
25 | 0 | dic = (struct erofs_inode_compact *)buf; |
26 | 0 | ifmt = le16_to_cpu(dic->i_format); |
27 | |
|
28 | 0 | vi->datalayout = erofs_inode_datalayout(ifmt); |
29 | 0 | if (vi->datalayout >= EROFS_INODE_DATALAYOUT_MAX) { |
30 | 0 | erofs_err("unsupported datalayout %u of nid %llu", |
31 | 0 | vi->datalayout, vi->nid | 0ULL); |
32 | 0 | return -EOPNOTSUPP; |
33 | 0 | } |
34 | 0 | switch (erofs_inode_version(ifmt)) { |
35 | 0 | case EROFS_INODE_LAYOUT_EXTENDED: |
36 | 0 | vi->inode_isize = sizeof(struct erofs_inode_extended); |
37 | |
|
38 | 0 | ret = erofs_dev_read(0, buf + sizeof(*dic), |
39 | 0 | inode_loc + sizeof(*dic), |
40 | 0 | sizeof(*die) - sizeof(*dic)); |
41 | 0 | if (ret < 0) |
42 | 0 | return -EIO; |
43 | | |
44 | 0 | die = (struct erofs_inode_extended *)buf; |
45 | 0 | vi->xattr_isize = erofs_xattr_ibody_size(die->i_xattr_icount); |
46 | 0 | vi->i_mode = le16_to_cpu(die->i_mode); |
47 | |
|
48 | 0 | switch (vi->i_mode & S_IFMT) { |
49 | 0 | case S_IFREG: |
50 | 0 | case S_IFDIR: |
51 | 0 | case S_IFLNK: |
52 | 0 | vi->u.i_blkaddr = le32_to_cpu(die->i_u.raw_blkaddr); |
53 | 0 | break; |
54 | 0 | case S_IFCHR: |
55 | 0 | case S_IFBLK: |
56 | 0 | vi->u.i_rdev = |
57 | 0 | erofs_new_decode_dev(le32_to_cpu(die->i_u.rdev)); |
58 | 0 | break; |
59 | 0 | case S_IFIFO: |
60 | 0 | case S_IFSOCK: |
61 | 0 | vi->u.i_rdev = 0; |
62 | 0 | break; |
63 | 0 | default: |
64 | 0 | goto bogusimode; |
65 | 0 | } |
66 | | |
67 | 0 | vi->i_uid = le32_to_cpu(die->i_uid); |
68 | 0 | vi->i_gid = le32_to_cpu(die->i_gid); |
69 | 0 | vi->i_nlink = le32_to_cpu(die->i_nlink); |
70 | |
|
71 | 0 | vi->i_mtime = le64_to_cpu(die->i_mtime); |
72 | 0 | vi->i_mtime_nsec = le64_to_cpu(die->i_mtime_nsec); |
73 | 0 | vi->i_size = le64_to_cpu(die->i_size); |
74 | 0 | if (vi->datalayout == EROFS_INODE_CHUNK_BASED) |
75 | | /* fill chunked inode summary info */ |
76 | 0 | vi->u.chunkformat = le16_to_cpu(die->i_u.c.format); |
77 | 0 | break; |
78 | 0 | case EROFS_INODE_LAYOUT_COMPACT: |
79 | 0 | vi->inode_isize = sizeof(struct erofs_inode_compact); |
80 | 0 | vi->xattr_isize = erofs_xattr_ibody_size(dic->i_xattr_icount); |
81 | 0 | vi->i_mode = le16_to_cpu(dic->i_mode); |
82 | |
|
83 | 0 | switch (vi->i_mode & S_IFMT) { |
84 | 0 | case S_IFREG: |
85 | 0 | case S_IFDIR: |
86 | 0 | case S_IFLNK: |
87 | 0 | vi->u.i_blkaddr = le32_to_cpu(dic->i_u.raw_blkaddr); |
88 | 0 | break; |
89 | 0 | case S_IFCHR: |
90 | 0 | case S_IFBLK: |
91 | 0 | vi->u.i_rdev = |
92 | 0 | erofs_new_decode_dev(le32_to_cpu(dic->i_u.rdev)); |
93 | 0 | break; |
94 | 0 | case S_IFIFO: |
95 | 0 | case S_IFSOCK: |
96 | 0 | vi->u.i_rdev = 0; |
97 | 0 | break; |
98 | 0 | default: |
99 | 0 | goto bogusimode; |
100 | 0 | } |
101 | | |
102 | 0 | vi->i_uid = le16_to_cpu(dic->i_uid); |
103 | 0 | vi->i_gid = le16_to_cpu(dic->i_gid); |
104 | 0 | vi->i_nlink = le16_to_cpu(dic->i_nlink); |
105 | |
|
106 | 0 | vi->i_mtime = sbi.build_time; |
107 | 0 | vi->i_mtime_nsec = sbi.build_time_nsec; |
108 | |
|
109 | 0 | vi->i_size = le32_to_cpu(dic->i_size); |
110 | 0 | if (vi->datalayout == EROFS_INODE_CHUNK_BASED) |
111 | 0 | vi->u.chunkformat = le16_to_cpu(dic->i_u.c.format); |
112 | 0 | break; |
113 | 0 | default: |
114 | 0 | erofs_err("unsupported on-disk inode version %u of nid %llu", |
115 | 0 | erofs_inode_version(ifmt), vi->nid | 0ULL); |
116 | 0 | return -EOPNOTSUPP; |
117 | 0 | } |
118 | | |
119 | 0 | vi->flags = 0; |
120 | 0 | if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { |
121 | 0 | if (vi->u.chunkformat & ~EROFS_CHUNK_FORMAT_ALL) { |
122 | 0 | erofs_err("unsupported chunk format %x of nid %llu", |
123 | 0 | vi->u.chunkformat, vi->nid | 0ULL); |
124 | 0 | return -EOPNOTSUPP; |
125 | 0 | } |
126 | 0 | vi->u.chunkbits = sbi.blkszbits + |
127 | 0 | (vi->u.chunkformat & EROFS_CHUNK_FORMAT_BLKBITS_MASK); |
128 | 0 | } else if (erofs_inode_is_data_compressed(vi->datalayout)) { |
129 | 0 | if (erofs_blksiz() != EROFS_MAX_BLOCK_SIZE) |
130 | 0 | return -EOPNOTSUPP; |
131 | 0 | return z_erofs_fill_inode(vi); |
132 | 0 | } |
133 | 0 | return 0; |
134 | 0 | bogusimode: |
135 | 0 | erofs_err("bogus i_mode (%o) @ nid %llu", vi->i_mode, vi->nid | 0ULL); |
136 | 0 | return -EFSCORRUPTED; |
137 | 0 | } |
138 | | |
139 | | struct erofs_dirent *find_target_dirent(erofs_nid_t pnid, |
140 | | void *dentry_blk, |
141 | | const char *name, unsigned int len, |
142 | | unsigned int nameoff, |
143 | | unsigned int maxsize) |
144 | 0 | { |
145 | 0 | struct erofs_dirent *de = dentry_blk; |
146 | 0 | const struct erofs_dirent *end = dentry_blk + nameoff; |
147 | |
|
148 | 0 | while (de < end) { |
149 | 0 | const char *de_name; |
150 | 0 | unsigned int de_namelen; |
151 | |
|
152 | 0 | nameoff = le16_to_cpu(de->nameoff); |
153 | 0 | de_name = (char *)dentry_blk + nameoff; |
154 | | |
155 | | /* the last dirent in the block? */ |
156 | 0 | if (de + 1 >= end) |
157 | 0 | de_namelen = strnlen(de_name, maxsize - nameoff); |
158 | 0 | else |
159 | 0 | de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; |
160 | | |
161 | | /* a corrupted entry is found */ |
162 | 0 | if (nameoff + de_namelen > maxsize || |
163 | 0 | de_namelen > EROFS_NAME_LEN) { |
164 | 0 | erofs_err("bogus dirent @ nid %llu", pnid | 0ULL); |
165 | 0 | DBG_BUGON(1); |
166 | 0 | return ERR_PTR(-EFSCORRUPTED); |
167 | 0 | } |
168 | | |
169 | 0 | if (len == de_namelen && !memcmp(de_name, name, de_namelen)) |
170 | 0 | return de; |
171 | 0 | ++de; |
172 | 0 | } |
173 | 0 | return NULL; |
174 | 0 | } |
175 | | |
176 | | struct nameidata { |
177 | | erofs_nid_t nid; |
178 | | unsigned int ftype; |
179 | | }; |
180 | | |
181 | | int erofs_namei(struct nameidata *nd, const char *name, unsigned int len) |
182 | 0 | { |
183 | 0 | erofs_nid_t nid = nd->nid; |
184 | 0 | int ret; |
185 | 0 | char buf[EROFS_MAX_BLOCK_SIZE]; |
186 | 0 | struct erofs_inode vi = { .nid = nid }; |
187 | 0 | erofs_off_t offset; |
188 | |
|
189 | 0 | ret = erofs_read_inode_from_disk(&vi); |
190 | 0 | if (ret) |
191 | 0 | return ret; |
192 | | |
193 | 0 | offset = 0; |
194 | 0 | while (offset < vi.i_size) { |
195 | 0 | erofs_off_t maxsize = min_t(erofs_off_t, |
196 | 0 | vi.i_size - offset, erofs_blksiz()); |
197 | 0 | struct erofs_dirent *de = (void *)buf; |
198 | 0 | unsigned int nameoff; |
199 | |
|
200 | 0 | ret = erofs_pread(&vi, buf, maxsize, offset); |
201 | 0 | if (ret) |
202 | 0 | return ret; |
203 | | |
204 | 0 | nameoff = le16_to_cpu(de->nameoff); |
205 | 0 | if (nameoff < sizeof(struct erofs_dirent) || |
206 | 0 | nameoff >= erofs_blksiz()) { |
207 | 0 | erofs_err("invalid de[0].nameoff %u @ nid %llu", |
208 | 0 | nameoff, nid | 0ULL); |
209 | 0 | return -EFSCORRUPTED; |
210 | 0 | } |
211 | | |
212 | 0 | de = find_target_dirent(nid, buf, name, len, |
213 | 0 | nameoff, maxsize); |
214 | 0 | if (IS_ERR(de)) |
215 | 0 | return PTR_ERR(de); |
216 | | |
217 | 0 | if (de) { |
218 | 0 | nd->nid = le64_to_cpu(de->nid); |
219 | 0 | return 0; |
220 | 0 | } |
221 | 0 | offset += maxsize; |
222 | 0 | } |
223 | 0 | return -ENOENT; |
224 | 0 | } |
225 | | |
226 | | static int link_path_walk(const char *name, struct nameidata *nd) |
227 | 0 | { |
228 | 0 | nd->nid = sbi.root_nid; |
229 | |
|
230 | 0 | while (*name == '/') |
231 | 0 | name++; |
232 | | |
233 | | /* At this point we know we have a real path component. */ |
234 | 0 | while (*name != '\0') { |
235 | 0 | const char *p = name; |
236 | 0 | int ret; |
237 | |
|
238 | 0 | do { |
239 | 0 | ++p; |
240 | 0 | } while (*p != '\0' && *p != '/'); |
241 | |
|
242 | 0 | DBG_BUGON(p <= name); |
243 | 0 | ret = erofs_namei(nd, name, p - name); |
244 | 0 | if (ret) |
245 | 0 | return ret; |
246 | | |
247 | 0 | name = p; |
248 | | /* Skip until no more slashes. */ |
249 | 0 | for (name = p; *name == '/'; ++name) |
250 | 0 | ; |
251 | 0 | } |
252 | 0 | return 0; |
253 | 0 | } |
254 | | |
255 | | int erofs_ilookup(const char *path, struct erofs_inode *vi) |
256 | 0 | { |
257 | 0 | int ret; |
258 | 0 | struct nameidata nd; |
259 | |
|
260 | 0 | ret = link_path_walk(path, &nd); |
261 | 0 | if (ret) |
262 | 0 | return ret; |
263 | | |
264 | 0 | vi->nid = nd.nid; |
265 | 0 | return erofs_read_inode_from_disk(vi); |
266 | 0 | } |