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