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