xref: /aosp_15_r20/external/erofs-utils/lib/namei.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
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