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