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, erofs_pos(start), 29 erofs_pos(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_MAX_BLOCK_SIZE]; 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_MAX_BLOCK_SIZE, 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_MAX_BLOCK_SIZE) { 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 = erofs_pos(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} 268