uboot/fs/erofs/fs.c
<<
>>
Prefs
   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}
 268