blob: dcdc883e34c43f6178c46be49b70f9218b34f151 [file] [log] [blame]
Huang Jianan024fb2f2022-02-26 15:05:47 +08001// SPDX-License-Identifier: GPL-2.0+
2#include "internal.h"
3#include <fs_internal.h>
4
5struct erofs_sb_info sbi;
6
7static struct erofs_ctxt {
8 struct disk_partition cur_part_info;
9 struct blk_desc *cur_dev;
10} ctxt;
11
12int erofs_dev_read(int device_id, void *buf, u64 offset, size_t len)
13{
14 lbaint_t sect = offset >> ctxt.cur_dev->log2blksz;
15 int off = offset & (ctxt.cur_dev->blksz - 1);
16
17 if (!ctxt.cur_dev)
18 return -EIO;
19
20 if (fs_devread(ctxt.cur_dev, &ctxt.cur_part_info, sect,
21 off, len, buf))
22 return 0;
23 return -EIO;
24}
25
26int erofs_blk_read(void *buf, erofs_blk_t start, u32 nblocks)
27{
Yifan Zhao39ace5c2023-07-07 23:52:12 +080028 return erofs_dev_read(0, buf, erofs_pos(start),
29 erofs_pos(nblocks));
Huang Jianan024fb2f2022-02-26 15:05:47 +080030}
31
32int erofs_probe(struct blk_desc *fs_dev_desc,
33 struct disk_partition *fs_partition)
34{
35 int ret;
36
37 ctxt.cur_dev = fs_dev_desc;
38 ctxt.cur_part_info = *fs_partition;
39
40 ret = erofs_read_superblock();
41 if (ret)
42 goto error;
43
44 return 0;
45error:
46 ctxt.cur_dev = NULL;
47 return ret;
48}
49
50struct erofs_dir_stream {
51 struct fs_dir_stream fs_dirs;
52 struct fs_dirent dirent;
53
54 struct erofs_inode inode;
Yifan Zhao39ace5c2023-07-07 23:52:12 +080055 char dblk[EROFS_MAX_BLOCK_SIZE];
Huang Jianan024fb2f2022-02-26 15:05:47 +080056 unsigned int maxsize, de_end;
57 erofs_off_t pos;
58};
59
60static int erofs_readlink(struct erofs_inode *vi)
61{
Gao Xiang89c59362025-02-13 19:28:47 +080062 size_t alloc_size;
Huang Jianan024fb2f2022-02-26 15:05:47 +080063 char *target;
64 int err;
65
Gao Xiang89c59362025-02-13 19:28:47 +080066 if (__builtin_add_overflow(vi->i_size, 1, &alloc_size))
67 return -EFSCORRUPTED;
68
69 target = malloc(alloc_size);
Huang Jianan024fb2f2022-02-26 15:05:47 +080070 if (!target)
71 return -ENOMEM;
Gao Xiang89c59362025-02-13 19:28:47 +080072 target[vi->i_size] = '\0';
Huang Jianan024fb2f2022-02-26 15:05:47 +080073
Gao Xiang89c59362025-02-13 19:28:47 +080074 err = erofs_pread(vi, target, vi->i_size, 0);
Huang Jianan024fb2f2022-02-26 15:05:47 +080075 if (err)
76 goto err_out;
77
78 err = erofs_ilookup(target, vi);
79 if (err)
80 goto err_out;
81
82err_out:
83 free(target);
84 return err;
85}
86
87int erofs_opendir(const char *filename, struct fs_dir_stream **dirsp)
88{
89 struct erofs_dir_stream *dirs;
90 int err;
91
92 dirs = calloc(1, sizeof(*dirs));
93 if (!dirs)
94 return -ENOMEM;
95
96 err = erofs_ilookup(filename, &dirs->inode);
97 if (err)
98 goto err_out;
99
100 if (S_ISLNK(dirs->inode.i_mode)) {
101 err = erofs_readlink(&dirs->inode);
102 if (err)
103 goto err_out;
104 }
105
106 if (!S_ISDIR(dirs->inode.i_mode)) {
107 err = -ENOTDIR;
108 goto err_out;
109 }
110 *dirsp = (struct fs_dir_stream *)dirs;
111 return 0;
112err_out:
113 free(dirs);
114 return err;
115}
116
117int erofs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
118{
119 struct erofs_dir_stream *dirs = (struct erofs_dir_stream *)fs_dirs;
120 struct fs_dirent *dent = &dirs->dirent;
121 erofs_off_t pos = dirs->pos;
122 unsigned int nameoff, de_namelen;
123 struct erofs_dirent *de;
124 char *de_name;
125 int err;
126
127 if (pos >= dirs->inode.i_size)
128 return 1;
129
130 if (!dirs->maxsize) {
Yifan Zhao39ace5c2023-07-07 23:52:12 +0800131 dirs->maxsize = min_t(unsigned int, EROFS_MAX_BLOCK_SIZE,
Huang Jianan024fb2f2022-02-26 15:05:47 +0800132 dirs->inode.i_size - pos);
133
134 err = erofs_pread(&dirs->inode, dirs->dblk,
135 dirs->maxsize, pos);
136 if (err)
137 return err;
138
139 de = (struct erofs_dirent *)dirs->dblk;
140 dirs->de_end = le16_to_cpu(de->nameoff);
141 if (dirs->de_end < sizeof(struct erofs_dirent) ||
Yifan Zhao39ace5c2023-07-07 23:52:12 +0800142 dirs->de_end >= EROFS_MAX_BLOCK_SIZE) {
Huang Jianan024fb2f2022-02-26 15:05:47 +0800143 erofs_err("invalid de[0].nameoff %u @ nid %llu",
144 dirs->de_end, de->nid | 0ULL);
145 return -EFSCORRUPTED;
146 }
147 }
148
149 de = (struct erofs_dirent *)(dirs->dblk + erofs_blkoff(pos));
150 nameoff = le16_to_cpu(de->nameoff);
151 de_name = (char *)dirs->dblk + nameoff;
152
153 /* the last dirent in the block? */
154 if (de + 1 >= (struct erofs_dirent *)(dirs->dblk + dirs->de_end))
155 de_namelen = strnlen(de_name, dirs->maxsize - nameoff);
156 else
157 de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
158
159 /* a corrupted entry is found */
160 if (nameoff + de_namelen > dirs->maxsize ||
161 de_namelen > EROFS_NAME_LEN) {
162 erofs_err("bogus dirent @ nid %llu", de->nid | 0ULL);
163 DBG_BUGON(1);
164 return -EFSCORRUPTED;
165 }
166
167 memcpy(dent->name, de_name, de_namelen);
168 dent->name[de_namelen] = '\0';
169
170 if (de->file_type == EROFS_FT_DIR) {
171 dent->type = FS_DT_DIR;
172 } else if (de->file_type == EROFS_FT_SYMLINK) {
173 dent->type = FS_DT_LNK;
174 } else {
175 struct erofs_inode vi;
176
177 dent->type = FS_DT_REG;
178 vi.nid = de->nid;
179
180 err = erofs_read_inode_from_disk(&vi);
181 if (err)
182 return err;
183 dent->size = vi.i_size;
184 }
185 *dentp = dent;
186
187 pos += sizeof(*de);
188 if (erofs_blkoff(pos) >= dirs->de_end) {
Yifan Zhao39ace5c2023-07-07 23:52:12 +0800189 pos = erofs_pos(erofs_blknr(pos) + 1);
Huang Jianan024fb2f2022-02-26 15:05:47 +0800190 dirs->maxsize = 0;
191 }
192 dirs->pos = pos;
193 return 0;
194}
195
196void erofs_closedir(struct fs_dir_stream *fs_dirs)
197{
198 free(fs_dirs);
199}
200
201int erofs_exists(const char *filename)
202{
203 struct erofs_inode vi;
204 int err;
205
206 err = erofs_ilookup(filename, &vi);
207 return err == 0;
208}
209
210int erofs_size(const char *filename, loff_t *size)
211{
212 struct erofs_inode vi;
213 int err;
214
215 err = erofs_ilookup(filename, &vi);
216 if (err)
217 return err;
218 *size = vi.i_size;
219 return 0;
220}
221
222int erofs_read(const char *filename, void *buf, loff_t offset, loff_t len,
223 loff_t *actread)
224{
225 struct erofs_inode vi;
226 int err;
227
228 err = erofs_ilookup(filename, &vi);
229 if (err)
230 return err;
231
232 if (S_ISLNK(vi.i_mode)) {
233 err = erofs_readlink(&vi);
234 if (err)
235 return err;
236 }
237
238 if (!len)
239 len = vi.i_size;
240
241 err = erofs_pread(&vi, buf, len, offset);
242 if (err) {
243 *actread = 0;
244 return err;
245 }
246
247 if (offset >= vi.i_size)
248 *actread = 0;
249 else if (offset + len > vi.i_size)
250 *actread = vi.i_size - offset;
251 else
252 *actread = len;
253 return 0;
254}
255
256void erofs_close(void)
257{
258 ctxt.cur_dev = NULL;
259}
260
261int erofs_uuid(char *uuid_str)
262{
263 if (IS_ENABLED(CONFIG_LIB_UUID)) {
264 if (ctxt.cur_dev)
265 uuid_bin_to_str(sbi.uuid, uuid_str,
266 UUID_STR_FORMAT_STD);
267 return 0;
268 }
269 return -ENOSYS;
270}