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