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