1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-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 return 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
find_target_dirent(erofs_nid_t pnid,void * dentry_blk,const char * name,unsigned int len,unsigned int nameoff,unsigned int maxsize)147 struct erofs_dirent *find_target_dirent(erofs_nid_t pnid,
148 void *dentry_blk,
149 const char *name, unsigned int len,
150 unsigned int nameoff,
151 unsigned int maxsize)
152 {
153 struct erofs_dirent *de = dentry_blk;
154 const struct erofs_dirent *end = dentry_blk + nameoff;
155
156 while (de < end) {
157 const char *de_name;
158 unsigned int de_namelen;
159
160 nameoff = le16_to_cpu(de->nameoff);
161 de_name = (char *)dentry_blk + nameoff;
162
163 /* the last dirent in the block? */
164 if (de + 1 >= end)
165 de_namelen = strnlen(de_name, maxsize - nameoff);
166 else
167 de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
168
169 /* a corrupted entry is found */
170 if (nameoff + de_namelen > maxsize ||
171 de_namelen > EROFS_NAME_LEN) {
172 erofs_err("bogus dirent @ nid %llu", pnid | 0ULL);
173 DBG_BUGON(1);
174 return ERR_PTR(-EFSCORRUPTED);
175 }
176
177 if (len == de_namelen && !memcmp(de_name, name, de_namelen))
178 return de;
179 ++de;
180 }
181 return NULL;
182 }
183
184 struct nameidata {
185 erofs_nid_t nid;
186 unsigned int ftype;
187 };
188
erofs_namei(struct nameidata * nd,const char * name,unsigned int len)189 int erofs_namei(struct nameidata *nd,
190 const char *name, unsigned int len)
191 {
192 erofs_nid_t nid = nd->nid;
193 int ret;
194 char buf[EROFS_BLKSIZ];
195 struct erofs_inode vi = { .nid = nid };
196 erofs_off_t offset;
197
198 ret = erofs_read_inode_from_disk(&vi);
199 if (ret)
200 return ret;
201
202 offset = 0;
203 while (offset < vi.i_size) {
204 erofs_off_t maxsize = min_t(erofs_off_t,
205 vi.i_size - offset, EROFS_BLKSIZ);
206 struct erofs_dirent *de = (void *)buf;
207 unsigned int nameoff;
208
209 ret = erofs_pread(&vi, buf, maxsize, offset);
210 if (ret)
211 return ret;
212
213 nameoff = le16_to_cpu(de->nameoff);
214 if (nameoff < sizeof(struct erofs_dirent) ||
215 nameoff >= EROFS_BLKSIZ) {
216 erofs_err("invalid de[0].nameoff %u @ nid %llu",
217 nameoff, nid | 0ULL);
218 return -EFSCORRUPTED;
219 }
220
221 de = find_target_dirent(nid, buf, name, len,
222 nameoff, maxsize);
223 if (IS_ERR(de))
224 return PTR_ERR(de);
225
226 if (de) {
227 nd->nid = le64_to_cpu(de->nid);
228 return 0;
229 }
230 offset += maxsize;
231 }
232 return -ENOENT;
233 }
234
link_path_walk(const char * name,struct nameidata * nd)235 static int link_path_walk(const char *name, struct nameidata *nd)
236 {
237 nd->nid = sbi.root_nid;
238
239 while (*name == '/')
240 name++;
241
242 /* At this point we know we have a real path component. */
243 while (*name != '\0') {
244 const char *p = name;
245 int ret;
246
247 do {
248 ++p;
249 } while (*p != '\0' && *p != '/');
250
251 DBG_BUGON(p <= name);
252 ret = erofs_namei(nd, name, p - name);
253 if (ret)
254 return ret;
255
256 name = p;
257 /* Skip until no more slashes. */
258 for (name = p; *name == '/'; ++name)
259 ;
260 }
261 return 0;
262 }
263
erofs_ilookup(const char * path,struct erofs_inode * vi)264 int erofs_ilookup(const char *path, struct erofs_inode *vi)
265 {
266 int ret;
267 struct nameidata nd;
268
269 ret = link_path_walk(path, &nd);
270 if (ret)
271 return ret;
272
273 vi->nid = nd.nid;
274 return erofs_read_inode_from_disk(vi);
275 }
276