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