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