]> git.ipfire.org Git - thirdparty/u-boot.git/blame - fs/erofs/fs.c
fs/erofs: add erofs filesystem support
[thirdparty/u-boot.git] / fs / erofs / fs.c
CommitLineData
830613f8
HJ
1// 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{
28 return erofs_dev_read(0, buf, blknr_to_addr(start),
29 blknr_to_addr(nblocks));
30}
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;
55 char dblk[EROFS_BLKSIZ];
56 unsigned int maxsize, de_end;
57 erofs_off_t pos;
58};
59
60static 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
79err_out:
80 free(target);
81 return err;
82}
83
84int 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;
109err_out:
110 free(dirs);
111 return err;
112}
113
114int 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
193void erofs_closedir(struct fs_dir_stream *fs_dirs)
194{
195 free(fs_dirs);
196}
197
198int 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
207int 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
219int 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
253void erofs_close(void)
254{
255 ctxt.cur_dev = NULL;
256}
257
258int 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}