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