| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * erofs-utils/fuse/dir.c |
| * |
| * Created by Li Guifu <blucerlee@gmail.com> |
| */ |
| #include <fuse.h> |
| #include <fuse_opt.h> |
| |
| #include "erofs/internal.h" |
| #include "erofs/print.h" |
| |
| static int erofs_fill_dentries(struct erofs_inode *dir, |
| fuse_fill_dir_t filler, void *buf, |
| void *dblk, unsigned int nameoff, |
| unsigned int maxsize) |
| { |
| struct erofs_dirent *de = dblk; |
| const struct erofs_dirent *end = dblk + nameoff; |
| char namebuf[EROFS_NAME_LEN + 1]; |
| |
| while (de < end) { |
| const char *de_name; |
| unsigned int de_namelen; |
| |
| nameoff = le16_to_cpu(de->nameoff); |
| de_name = (char *)dblk + nameoff; |
| |
| /* the last dirent in the block? */ |
| if (de + 1 >= end) |
| de_namelen = strnlen(de_name, maxsize - nameoff); |
| else |
| de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; |
| |
| /* a corrupted entry is found */ |
| if (nameoff + de_namelen > maxsize || |
| de_namelen > EROFS_NAME_LEN) { |
| erofs_err("bogus dirent @ nid %llu", dir->nid | 0ULL); |
| DBG_BUGON(1); |
| return -EFSCORRUPTED; |
| } |
| |
| memcpy(namebuf, de_name, de_namelen); |
| namebuf[de_namelen] = '\0'; |
| |
| filler(buf, namebuf, NULL, 0); |
| ++de; |
| } |
| return 0; |
| } |
| |
| int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, |
| off_t offset, struct fuse_file_info *fi) |
| { |
| int ret; |
| struct erofs_inode dir; |
| char dblk[EROFS_BLKSIZ]; |
| erofs_off_t pos; |
| |
| erofs_dbg("readdir:%s offset=%llu", path, (long long)offset); |
| |
| ret = erofs_ilookup(path, &dir); |
| if (ret) |
| return ret; |
| |
| erofs_dbg("path=%s nid = %llu", path, dir.nid | 0ULL); |
| |
| if (!S_ISDIR(dir.i_mode)) |
| return -ENOTDIR; |
| |
| if (!dir.i_size) |
| return 0; |
| |
| pos = 0; |
| while (pos < dir.i_size) { |
| unsigned int nameoff, maxsize; |
| struct erofs_dirent *de; |
| |
| maxsize = min_t(unsigned int, EROFS_BLKSIZ, |
| dir.i_size - pos); |
| ret = erofs_pread(&dir, dblk, maxsize, pos); |
| if (ret) |
| return ret; |
| |
| de = (struct erofs_dirent *)dblk; |
| nameoff = le16_to_cpu(de->nameoff); |
| if (nameoff < sizeof(struct erofs_dirent) || |
| nameoff >= PAGE_SIZE) { |
| erofs_err("invalid de[0].nameoff %u @ nid %llu", |
| nameoff, dir.nid | 0ULL); |
| ret = -EFSCORRUPTED; |
| break; |
| } |
| |
| ret = erofs_fill_dentries(&dir, filler, buf, |
| dblk, nameoff, maxsize); |
| if (ret) |
| break; |
| pos += maxsize; |
| } |
| return 0; |
| } |
| |