Merge tag 'xilinx-for-v2022.07-rc1' of https://source.denx.de/u-boot/custodians/u...
[platform/kernel/u-boot.git] / fs / erofs / fs.c
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
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
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
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
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
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
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
193 void erofs_closedir(struct fs_dir_stream *fs_dirs)
194 {
195         free(fs_dirs);
196 }
197
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
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
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
253 void erofs_close(void)
254 {
255         ctxt.cur_dev = NULL;
256 }
257
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 }